2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2007, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * SLA Implementation by:
9 * Russell Bryant <russell@digium.com>
11 * See http://www.asterisk.org for more information about
12 * the Asterisk project. Please do not directly contact
13 * any of the maintainers of this project for assistance;
14 * the project provides a web site, mailing lists and IRC
15 * channels for your use.
17 * This program is free software, distributed under the terms of
18 * the GNU General Public License Version 2. See the LICENSE file
19 * at the top of the source tree.
24 * \brief Meet me conference bridge and Shared Line Appearances
26 * \author Mark Spencer <markster@digium.com>
27 * \author (SLA) Russell Bryant <russell@digium.com>
29 * \ingroup applications
33 <depend>dahdi</depend>
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
40 #include <dahdi/user.h>
42 #include "asterisk/lock.h"
43 #include "asterisk/file.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/pbx.h"
46 #include "asterisk/module.h"
47 #include "asterisk/config.h"
48 #include "asterisk/app.h"
49 #include "asterisk/dsp.h"
50 #include "asterisk/musiconhold.h"
51 #include "asterisk/manager.h"
52 #include "asterisk/cli.h"
53 #include "asterisk/say.h"
54 #include "asterisk/utils.h"
55 #include "asterisk/translate.h"
56 #include "asterisk/ulaw.h"
57 #include "asterisk/astobj2.h"
58 #include "asterisk/devicestate.h"
59 #include "asterisk/dial.h"
60 #include "asterisk/causes.h"
61 #include "asterisk/paths.h"
67 <application name="MeetMe" language="en_US">
69 MeetMe conference bridge.
72 <parameter name="confno">
73 <para>The conference number</para>
75 <parameter name="options">
78 <para>Set admin mode.</para>
81 <para>Set marked mode.</para>
84 <para>Run AGI script specified in <variable>MEETME_AGI_BACKGROUND</variable>
85 Default: <literal>conf-background.agi</literal>.</para>
86 <note><para>This does not work with non-DAHDI channels in the same
87 conference).</para></note>
90 <para>Announce user(s) count on joining a conference.</para>
93 <para>Continue in dialplan when kicked out of conference.</para>
96 <para>Dynamically add conference.</para>
99 <para>Dynamically add conference, prompting for a PIN.</para>
102 <para>Select an empty conference.</para>
105 <para>Select an empty pinless conference.</para>
108 <para>Pass DTMF through the conference.</para>
111 <argument name="x" required="true">
112 <para>The file to playback</para>
114 <para>Play an intro announcement in conference.</para>
117 <para>Announce user join/leave with review.</para>
120 <para>Announce user join/leave without review.</para>
123 <para>Set listen only mode (Listen only, no talking).</para>
126 <para>Set initially muted.</para>
128 <option name="M" hasparams="optional">
129 <para>Enable music on hold when the conference has a single caller. Optionally,
130 specify a musiconhold class to use. If one is not provided, it will use the
131 channel's currently set music class, or <literal>default</literal>.</para>
132 <argument name="class" required="true" />
135 <para>Set talker optimization - treats talkers who aren't speaking as
136 being muted, meaning (a) No encode is done on transmission and (b)
137 Received audio that is not registered as talking is omitted causing no
138 buildup in background noise.</para>
140 <option name="p" hasparams="optional">
141 <para>Allow user to exit the conference by pressing <literal>#</literal> (default)
142 or any of the defined keys. If keys contain <literal>*</literal> this will override
143 option <literal>s</literal>. The key used is set to channel variable
144 <variable>MEETME_EXIT_KEY</variable>.</para>
145 <argument name="keys" required="true" />
148 <para>Always prompt for the pin even if it is specified.</para>
151 <para>Quiet mode (don't play enter/leave sounds).</para>
154 <para>Record conference (records as <variable>MEETME_RECORDINGFILE</variable>
155 using format <variable>MEETME_RECORDINGFORMAT</variable>. Default filename is
156 <literal>meetme-conf-rec-${CONFNO}-${UNIQUEID}</literal> and the default format is
160 <para>Present menu (user or admin) when <literal>*</literal> is received
161 (send to menu).</para>
164 <para>Set talk only mode. (Talk only, no listening).</para>
167 <para>Set talker detection (sent to manager interface and meetme list).</para>
169 <option name="w" hasparams="optional">
170 <para>Wait until the marked user enters the conference.</para>
171 <argument name="secs" required="true" />
174 <para>Close the conference when last marked user exits</para>
177 <para>Allow user to exit the conference by entering a valid single digit
178 extension <variable>MEETME_EXIT_CONTEXT</variable> or the current context
179 if that variable is not defined.</para>
182 <para>Do not play message when first person enters</para>
185 <para>Kick the user <replaceable>x</replaceable> seconds <emphasis>after</emphasis> he entered into
186 the conference.</para>
187 <argument name="x" required="true" />
189 <option name="L" argsep=":">
190 <para>Limit the conference to <replaceable>x</replaceable> ms. Play a warning when
191 <replaceable>y</replaceable> ms are left. Repeat the warning every <replaceable>z</replaceable> ms.
192 The following special variables can be used with this option:</para>
194 <variable name="CONF_LIMIT_TIMEOUT_FILE">
195 <para>File to play when time is up.</para>
197 <variable name="CONF_LIMIT_WARNING_FILE">
198 <para>File to play as warning if <replaceable>y</replaceable> is defined. The
199 default is to say the time remaining.</para>
202 <argument name="x" />
203 <argument name="y" />
204 <argument name="z" />
208 <parameter name="pin" />
211 <para>Enters the user into a specified MeetMe conference. If the <replaceable>confno</replaceable>
212 is omitted, the user will be prompted to enter one. User can exit the conference by hangup, or
213 if the <literal>p</literal> option is specified, by pressing <literal>#</literal>.</para>
214 <note><para>The DAHDI kernel modules and at least one hardware driver (or dahdi_dummy)
215 must be present for conferencing to operate properly. In addition, the chan_dahdi channel driver
216 must be loaded for the <literal>i</literal> and <literal>r</literal> options to operate at
220 <ref type="application">MeetMeCount</ref>
221 <ref type="application">MeetMeAdmin</ref>
222 <ref type="application">MeetMeChannelAdmin</ref>
225 <application name="MeetMeCount" language="en_US">
227 MeetMe participant count.
230 <parameter name="confno" required="true">
231 <para>Conference number.</para>
233 <parameter name="var" />
236 <para>Plays back the number of users in the specified MeetMe conference.
237 If <replaceable>var</replaceable> is specified, playback will be skipped and the value
238 will be returned in the variable. Upon application completion, MeetMeCount will hangup
239 the channel, unless priority <literal>n+1</literal> exists, in which case priority progress will
243 <ref type="application">MeetMe</ref>
246 <application name="MeetMeAdmin" language="en_US">
248 MeetMe conference administration.
251 <parameter name="confno" required="true" />
252 <parameter name="command" required="true">
255 <para>Eject last user that joined.</para>
258 <para>Extend conference end time, if scheduled.</para>
261 <para>Kick one user out of conference.</para>
264 <para>Kick all users out of conference.</para>
267 <para>Unlock conference.</para>
270 <para>Lock conference.</para>
273 <para>Unmute one user.</para>
276 <para>Mute one user.</para>
279 <para>Unmute all users in the conference.</para>
282 <para>Mute all non-admin users in the conference.</para>
285 <para>Reset one user's volume settings.</para>
288 <para>Reset all users volume settings.</para>
291 <para>Lower entire conference speaking volume.</para>
294 <para>Raise entire conference speaking volume.</para>
297 <para>Lower one user's talk volume.</para>
300 <para>Raise one user's talk volume.</para>
303 <para>Lower one user's listen volume.</para>
306 <para>Raise one user's listen volume.</para>
309 <para>Lower entire conference listening volume.</para>
312 <para>Raise entire conference listening volume.</para>
316 <parameter name="user" />
319 <para>Run admin <replaceable>command</replaceable> for conference <replaceable>confno</replaceable>.</para>
320 <para>Will additionally set the variable <variable>MEETMEADMINSTATUS</variable> with one of
321 the following values:</para>
323 <variable name="MEETMEADMINSTATUS">
324 <value name="NOPARSE">
327 <value name="NOTFOUND">
328 User specified was not found.
330 <value name="FAILED">
331 Another failure occurred.
334 The operation was completed successfully.
340 <ref type="application">MeetMe</ref>
343 <application name="MeetMeChannelAdmin" language="en_US">
345 MeetMe conference Administration (channel specific).
348 <parameter name="channel" required="true" />
349 <parameter name="command" required="true">
352 <para>Kick the specified user out of the conference he is in.</para>
355 <para>Unmute the specified user.</para>
358 <para>Mute the specified user.</para>
364 <para>Run admin <replaceable>command</replaceable> for a specific
365 <replaceable>channel</replaceable> in any coference.</para>
368 <application name="SLAStation" language="en_US">
370 Shared Line Appearance Station.
373 <parameter name="station" required="true">
374 <para>Station name</para>
378 <para>This application should be executed by an SLA station. The argument depends
379 on how the call was initiated. If the phone was just taken off hook, then the argument
380 <replaceable>station</replaceable> should be just the station name. If the call was
381 initiated by pressing a line key, then the station name should be preceded by an underscore
382 and the trunk name associated with that line button.</para>
383 <para>For example: <literal>station1_line1</literal></para>
384 <para>On exit, this application will set the variable <variable>SLASTATION_STATUS</variable> to
385 one of the following values:</para>
387 <variable name="SLASTATION_STATUS">
388 <value name="FAILURE" />
389 <value name="CONGESTION" />
390 <value name="SUCCESS" />
395 <application name="SLATrunk" language="en_US">
397 Shared Line Appearance Trunk.
400 <parameter name="trunk" required="true">
401 <para>Trunk name</para>
403 <parameter name="options">
405 <option name="M" hasparams="optional">
406 <para>Play back the specified MOH <replaceable>class</replaceable>
407 instead of ringing</para>
408 <argument name="class" required="true" />
414 <para>This application should be executed by an SLA trunk on an inbound call. The channel calling
415 this application should correspond to the SLA trunk with the name <replaceable>trunk</replaceable>
416 that is being passed as an argument.</para>
417 <para>On exit, this application will set the variable <variable>SLATRUNK_STATUS</variable> to
418 one of the following values:</para>
420 <variable name="SLATRUNK_STATUS">
421 <value name="FAILURE" />
422 <value name="SUCCESS" />
423 <value name="UNANSWERED" />
424 <value name="RINGTIMEOUT" />
429 <function name="MEETME_INFO" language="en_US">
431 Query a given conference of various properties.
434 <parameter name="keyword" required="true">
435 <para>Options:</para>
438 <para>Boolean of whether the corresponding conference is locked.</para>
440 <enum name="parties">
441 <para>Number of parties in a given conference</para>
443 <enum name="activity">
444 <para>Duration of conference in seconds.</para>
446 <enum name="dynamic">
447 <para>Boolean of whether the corresponding conference is dynamic.</para>
451 <parameter name="confno" required="true">
452 <para>Conference number to retrieve information from.</para>
457 <ref type="application">MeetMe</ref>
458 <ref type="application">MeetMeCount</ref>
459 <ref type="application">MeetMeAdmin</ref>
460 <ref type="application">MeetMeChannelAdmin</ref>
463 <manager name="MeetmeMute" language="en_US">
468 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
469 <parameter name="Meetme" required="true" />
470 <parameter name="Usernum" required="true" />
475 <manager name="MeetmeUnmute" language="en_US">
477 Unmute a Meetme user.
480 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
481 <parameter name="Meetme" required="true" />
482 <parameter name="Usernum" required="true" />
487 <manager name="MeetmeList" language="en_US">
489 List participants in a conference.
492 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
493 <parameter name="Conference" required="true">
494 <para>Conference number.</para>
498 <para>Lists all users in a particular MeetMe conference.
499 MeetmeList will follow as separate events, followed by a final event called
500 MeetmeListComplete.</para>
505 #define CONFIG_FILE_NAME "meetme.conf"
506 #define SLA_CONFIG_FILE "sla.conf"
508 /*! each buffer is 20ms, so this is 640ms total */
509 #define DEFAULT_AUDIO_BUFFERS 32
511 /*! String format for scheduled conferences */
512 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
515 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
516 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
517 ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */
518 /*! User has requested to speak */
519 ADMINFLAG_T_REQUEST = (1 << 4),
522 #define MEETME_DELAYDETECTTALK 300
523 #define MEETME_DELAYDETECTENDTALK 1000
525 #define AST_FRAME_BITS 32
532 enum entrance_sound {
537 enum recording_state {
539 MEETME_RECORD_STARTED,
540 MEETME_RECORD_ACTIVE,
541 MEETME_RECORD_TERMINATE
544 #define CONF_SIZE 320
547 /*! user has admin access on the conference */
548 CONFFLAG_ADMIN = (1 << 0),
549 /*! If set the user can only receive audio from the conference */
550 CONFFLAG_MONITOR = (1 << 1),
551 /*! If set asterisk will exit conference when key defined in p() option is pressed */
552 CONFFLAG_KEYEXIT = (1 << 2),
553 /*! If set asterisk will provide a menu to the user when '*' is pressed */
554 CONFFLAG_STARMENU = (1 << 3),
555 /*! If set the use can only send audio to the conference */
556 CONFFLAG_TALKER = (1 << 4),
557 /*! If set there will be no enter or leave sounds */
558 CONFFLAG_QUIET = (1 << 5),
559 /*! If set, when user joins the conference, they will be told the number
560 * of users that are already in */
561 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
562 /*! Set to run AGI Script in Background */
563 CONFFLAG_AGI = (1 << 7),
564 /*! Set to have music on hold when user is alone in conference */
565 CONFFLAG_MOH = (1 << 8),
566 /*! If set the MeetMe will return if all marked with this flag left */
567 CONFFLAG_MARKEDEXIT = (1 << 9),
568 /*! If set, the MeetMe will wait until a marked user enters */
569 CONFFLAG_WAITMARKED = (1 << 10),
570 /*! If set, the MeetMe will exit to the specified context */
571 CONFFLAG_EXIT_CONTEXT = (1 << 11),
572 /*! If set, the user will be marked */
573 CONFFLAG_MARKEDUSER = (1 << 12),
574 /*! If set, user will be ask record name on entry of conference */
575 CONFFLAG_INTROUSER = (1 << 13),
576 /*! If set, the MeetMe will be recorded */
577 CONFFLAG_RECORDCONF = (1<< 14),
578 /*! If set, the user will be monitored if the user is talking or not */
579 CONFFLAG_MONITORTALKER = (1 << 15),
580 CONFFLAG_DYNAMIC = (1 << 16),
581 CONFFLAG_DYNAMICPIN = (1 << 17),
582 CONFFLAG_EMPTY = (1 << 18),
583 CONFFLAG_EMPTYNOPIN = (1 << 19),
584 CONFFLAG_ALWAYSPROMPT = (1 << 20),
585 /*! If set, treat talking users as muted users */
586 CONFFLAG_OPTIMIZETALKER = (1 << 21),
587 /*! If set, won't speak the extra prompt when the first person
588 * enters the conference */
589 CONFFLAG_NOONLYPERSON = (1 << 22),
590 /*! If set, user will be asked to record name on entry of conference
592 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
593 /*! If set, the user will be initially self-muted */
594 CONFFLAG_STARTMUTED = (1 << 24),
595 /*! Pass DTMF through the conference */
596 CONFFLAG_PASS_DTMF = (1 << 25),
597 CONFFLAG_SLA_STATION = (1 << 26),
598 CONFFLAG_SLA_TRUNK = (1 << 27),
599 /*! If set, the user should continue in the dialplan if kicked out */
600 CONFFLAG_KICK_CONTINUE = (1 << 28),
601 CONFFLAG_DURATION_STOP = (1 << 29),
602 CONFFLAG_DURATION_LIMIT = (1 << 30),
603 /*! Do not write any audio to this channel until the state is up. */
604 CONFFLAG_NO_AUDIO_UNTIL_UP = (1 << 31),
607 /* !If set play an intro announcement at start of conference */
608 #define CONFFLAG_INTROMSG ((uint64_t)1 << 32)
611 OPT_ARG_WAITMARKED = 0,
612 OPT_ARG_EXITKEYS = 1,
613 OPT_ARG_DURATION_STOP = 2,
614 OPT_ARG_DURATION_LIMIT = 3,
615 OPT_ARG_MOH_CLASS = 4,
616 OPT_ARG_INTROMSG = 5,
617 OPT_ARG_ARRAY_SIZE = 6,
620 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
621 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
622 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
623 AST_APP_OPTION('b', CONFFLAG_AGI ),
624 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
625 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
626 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
627 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
628 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
629 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
630 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
631 AST_APP_OPTION_ARG('G', CONFFLAG_INTROMSG, OPT_ARG_INTROMSG ),
632 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
633 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
634 AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
635 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
636 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
637 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
638 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
639 AST_APP_OPTION('q', CONFFLAG_QUIET ),
640 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
641 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
642 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
643 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
644 AST_APP_OPTION('t', CONFFLAG_TALKER ),
645 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
646 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
647 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
648 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
649 AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
650 AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
653 static const char * const app = "MeetMe";
654 static const char * const app2 = "MeetMeCount";
655 static const char * const app3 = "MeetMeAdmin";
656 static const char * const app4 = "MeetMeChannelAdmin";
657 static const char * const slastation_app = "SLAStation";
658 static const char * const slatrunk_app = "SLATrunk";
660 /* Lookup RealTime conferences based on confno and current time */
661 static int rt_schedule;
662 static int fuzzystart;
663 static int earlyalert;
667 /*! Log participant count to the RealTime backend */
668 static int rt_log_members;
670 #define MAX_CONFNUM 80
672 #define OPTIONS_LEN 100
674 /* Enough space for "<conference #>,<pin>,<admin pin>" followed by a 0 byte. */
675 #define MAX_SETTINGS (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)
682 struct announce_listitem {
683 AST_LIST_ENTRY(announce_listitem) entry;
684 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
685 char language[MAX_LANGUAGE];
686 struct ast_channel *confchan;
688 enum announcetypes announcetype;
691 /*! \brief The MeetMe Conference object */
692 struct ast_conference {
693 ast_mutex_t playlock; /*!< Conference specific lock (players) */
694 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
695 char confno[MAX_CONFNUM]; /*!< Conference */
696 struct ast_channel *chan; /*!< Announcements channel */
697 struct ast_channel *lchan; /*!< Listen/Record channel */
698 int fd; /*!< Announcements fd */
699 int dahdiconf; /*!< DAHDI Conf # */
700 int users; /*!< Number of active users */
701 int markedusers; /*!< Number of marked users */
702 int maxusers; /*!< Participant limit if scheduled */
703 int endalert; /*!< When to play conf ending message */
704 time_t start; /*!< Start time (s) */
705 int refcount; /*!< reference count of usage */
706 enum recording_state recording:2; /*!< recording status */
707 unsigned int isdynamic:1; /*!< Created on the fly? */
708 unsigned int locked:1; /*!< Is the conference locked? */
709 unsigned int gmuted:1; /*!< Is the conference globally muted? (all non-admins) */
710 pthread_t recordthread; /*!< thread for recording */
711 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
712 pthread_attr_t attr; /*!< thread attribute */
713 char *recordingfilename; /*!< Filename to record the Conference into */
714 char *recordingformat; /*!< Format to record the Conference in */
715 char pin[MAX_PIN]; /*!< If protected by a PIN */
716 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
718 long endtime; /*!< When to end the conf if scheduled */
719 const char *useropts; /*!< RealTime user flags */
720 const char *adminopts; /*!< RealTime moderator flags */
721 const char *bookid; /*!< RealTime conference id */
722 struct ast_frame *transframe[32];
723 struct ast_frame *origframe;
724 struct ast_trans_pvt *transpath[32];
725 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
726 AST_LIST_ENTRY(ast_conference) list;
727 /* announce_thread related data */
728 pthread_t announcethread;
729 ast_mutex_t announcethreadlock;
730 unsigned int announcethread_stop:1;
731 ast_cond_t announcelist_addition;
732 AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
733 ast_mutex_t announcelistlock;
736 static AST_LIST_HEAD_STATIC(confs, ast_conference);
738 static unsigned int conf_map[1024] = {0, };
741 int desired; /*!< Desired volume adjustment */
742 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
745 /*! \brief The MeetMe User object */
746 struct ast_conf_user {
747 int user_no; /*!< User Number */
748 struct ast_flags64 userflags; /*!< Flags as set in the conference */
749 int adminflags; /*!< Flags set by the Admin */
750 struct ast_channel *chan; /*!< Connected channel */
751 int talking; /*!< Is user talking */
752 int dahdichannel; /*!< Is a DAHDI channel */
753 char usrvalue[50]; /*!< Custom User Value */
754 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
755 time_t jointime; /*!< Time the user joined the conference */
756 time_t kicktime; /*!< Time the user will be kicked from the conference */
757 struct timeval start_time; /*!< Time the user entered into the conference */
758 long timelimit; /*!< Time limit for the user to be in the conference L(x:y:z) */
759 long play_warning; /*!< Play a warning when 'y' ms are left */
760 long warning_freq; /*!< Repeat the warning every 'z' ms */
761 const char *warning_sound; /*!< File to play as warning if 'y' is defined */
762 const char *end_sound; /*!< File to play when time is up. */
764 struct volume listen;
765 AST_LIST_ENTRY(ast_conf_user) list;
768 enum sla_which_trunk_refs {
773 enum sla_trunk_state {
774 SLA_TRUNK_STATE_IDLE,
775 SLA_TRUNK_STATE_RINGING,
777 SLA_TRUNK_STATE_ONHOLD,
778 SLA_TRUNK_STATE_ONHOLD_BYME,
781 enum sla_hold_access {
782 /*! This means that any station can put it on hold, and any station
783 * can retrieve the call from hold. */
785 /*! This means that only the station that put the call on hold may
786 * retrieve it from hold. */
790 struct sla_trunk_ref;
793 AST_RWLIST_ENTRY(sla_station) entry;
794 AST_DECLARE_STRING_FIELDS(
795 AST_STRING_FIELD(name);
796 AST_STRING_FIELD(device);
797 AST_STRING_FIELD(autocontext);
799 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
800 struct ast_dial *dial;
801 /*! Ring timeout for this station, for any trunk. If a ring timeout
802 * is set for a specific trunk on this station, that will take
803 * priority over this value. */
804 unsigned int ring_timeout;
805 /*! Ring delay for this station, for any trunk. If a ring delay
806 * is set for a specific trunk on this station, that will take
807 * priority over this value. */
808 unsigned int ring_delay;
809 /*! This option uses the values in the sla_hold_access enum and sets the
810 * access control type for hold on this station. */
811 unsigned int hold_access:1;
812 /*! Use count for inside sla_station_exec */
813 unsigned int ref_count;
816 struct sla_station_ref {
817 AST_LIST_ENTRY(sla_station_ref) entry;
818 struct sla_station *station;
822 AST_RWLIST_ENTRY(sla_trunk) entry;
823 AST_DECLARE_STRING_FIELDS(
824 AST_STRING_FIELD(name);
825 AST_STRING_FIELD(device);
826 AST_STRING_FIELD(autocontext);
828 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
829 /*! Number of stations that use this trunk */
830 unsigned int num_stations;
831 /*! Number of stations currently on a call with this trunk */
832 unsigned int active_stations;
833 /*! Number of stations that have this trunk on hold. */
834 unsigned int hold_stations;
835 struct ast_channel *chan;
836 unsigned int ring_timeout;
837 /*! If set to 1, no station will be able to join an active call with
839 unsigned int barge_disabled:1;
840 /*! This option uses the values in the sla_hold_access enum and sets the
841 * access control type for hold on this trunk. */
842 unsigned int hold_access:1;
843 /*! Whether this trunk is currently on hold, meaning that once a station
844 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
845 unsigned int on_hold:1;
846 /*! Use count for inside sla_trunk_exec */
847 unsigned int ref_count;
850 struct sla_trunk_ref {
851 AST_LIST_ENTRY(sla_trunk_ref) entry;
852 struct sla_trunk *trunk;
853 enum sla_trunk_state state;
854 struct ast_channel *chan;
855 /*! Ring timeout to use when this trunk is ringing on this specific
856 * station. This takes higher priority than a ring timeout set at
857 * the station level. */
858 unsigned int ring_timeout;
859 /*! Ring delay to use when this trunk is ringing on this specific
860 * station. This takes higher priority than a ring delay set at
861 * the station level. */
862 unsigned int ring_delay;
865 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
866 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
868 static const char sla_registrar[] = "SLA";
870 /*! \brief Event types that can be queued up for the SLA thread */
871 enum sla_event_type {
872 /*! A station has put the call on hold */
874 /*! The state of a dial has changed */
875 SLA_EVENT_DIAL_STATE,
876 /*! The state of a ringing trunk has changed */
877 SLA_EVENT_RINGING_TRUNK,
878 /*! A reload of configuration has been requested */
880 /*! Poke the SLA thread so it can check if it can perform a reload */
881 SLA_EVENT_CHECK_RELOAD,
885 enum sla_event_type type;
886 struct sla_station *station;
887 struct sla_trunk_ref *trunk_ref;
888 AST_LIST_ENTRY(sla_event) entry;
891 /*! \brief A station that failed to be dialed
892 * \note Only used by the SLA thread. */
893 struct sla_failed_station {
894 struct sla_station *station;
895 struct timeval last_try;
896 AST_LIST_ENTRY(sla_failed_station) entry;
899 /*! \brief A trunk that is ringing */
900 struct sla_ringing_trunk {
901 struct sla_trunk *trunk;
902 /*! The time that this trunk started ringing */
903 struct timeval ring_begin;
904 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
905 AST_LIST_ENTRY(sla_ringing_trunk) entry;
908 enum sla_station_hangup {
909 SLA_STATION_HANGUP_NORMAL,
910 SLA_STATION_HANGUP_TIMEOUT,
913 /*! \brief A station that is ringing */
914 struct sla_ringing_station {
915 struct sla_station *station;
916 /*! The time that this station started ringing */
917 struct timeval ring_begin;
918 AST_LIST_ENTRY(sla_ringing_station) entry;
922 * \brief A structure for data used by the sla thread
925 /*! The SLA thread ID */
929 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
930 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
931 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
932 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
934 /*! Attempt to handle CallerID, even though it is known not to work
935 * properly in some situations. */
936 unsigned int attempt_callerid:1;
937 /*! A reload has been requested */
938 unsigned int reload:1;
940 .thread = AST_PTHREADT_NULL,
943 /*! \brief The number of audio buffers to be allocated on pseudo channels
944 * when in a conference */
945 static int audio_buffers;
947 /*! \brief Map 'volume' levels from -5 through +5 into decibel (dB)
948 * settings for channel drivers.
950 * \note these are not a straight linear-to-dB
951 * conversion... the numbers have been modified
952 * to give the user a better level of adjustability.
954 static const char gain_map[] = {
969 static int admin_exec(struct ast_channel *chan, const char *data);
970 static void *recordthread(void *args);
972 static const char *istalking(int x)
977 return "(unmonitored)";
979 return "(not talking)";
982 static int careful_write(int fd, unsigned char *data, int len, int block)
989 x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
990 res = ioctl(fd, DAHDI_IOMUX, &x);
994 res = write(fd, data, len);
996 if (errno != EAGAIN) {
997 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
1009 static int set_talk_volume(struct ast_conf_user *user, int volume)
1013 /* attempt to make the adjustment in the channel driver;
1014 if successful, don't adjust in the frame reading routine
1016 gain_adjust = gain_map[volume + 5];
1018 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1021 static int set_listen_volume(struct ast_conf_user *user, int volume)
1025 /* attempt to make the adjustment in the channel driver;
1026 if successful, don't adjust in the frame reading routine
1028 gain_adjust = gain_map[volume + 5];
1030 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1033 static void tweak_volume(struct volume *vol, enum volume_action action)
1037 switch (vol->desired) {
1052 switch (vol->desired) {
1068 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
1070 tweak_volume(&user->talk, action);
1071 /* attempt to make the adjustment in the channel driver;
1072 if successful, don't adjust in the frame reading routine
1074 if (!set_talk_volume(user, user->talk.desired))
1075 user->talk.actual = 0;
1077 user->talk.actual = user->talk.desired;
1080 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
1082 tweak_volume(&user->listen, action);
1083 /* attempt to make the adjustment in the channel driver;
1084 if successful, don't adjust in the frame reading routine
1086 if (!set_listen_volume(user, user->listen.desired))
1087 user->listen.actual = 0;
1089 user->listen.actual = user->listen.desired;
1092 static void reset_volumes(struct ast_conf_user *user)
1094 signed char zero_volume = 0;
1096 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
1097 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
1100 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
1102 unsigned char *data;
1106 if (!ast_check_hangup(chan))
1107 res = ast_autoservice_start(chan);
1109 AST_LIST_LOCK(&confs);
1114 len = sizeof(enter);
1118 len = sizeof(leave);
1125 careful_write(conf->fd, data, len, 1);
1128 AST_LIST_UNLOCK(&confs);
1131 ast_autoservice_stop(chan);
1135 * \brief Find or create a conference
1137 * \param confno The conference name/number
1138 * \param pin The regular user pin
1139 * \param pinadmin The admin pin
1140 * \param make Make the conf if it doesn't exist
1141 * \param dynamic Mark the newly created conference as dynamic
1142 * \param refcount How many references to mark on the conference
1143 * \param chan The asterisk channel
1145 * \return A pointer to the conference struct, or NULL if it wasn't found and
1146 * make or dynamic were not set.
1148 static struct ast_conference *build_conf(const char *confno, const char *pin,
1149 const char *pinadmin, int make, int dynamic, int refcount,
1150 const struct ast_channel *chan)
1152 struct ast_conference *cnf;
1153 struct dahdi_confinfo dahdic = { 0, };
1156 AST_LIST_LOCK(&confs);
1158 AST_LIST_TRAVERSE(&confs, cnf, list) {
1159 if (!strcmp(confno, cnf->confno))
1163 if (cnf || (!make && !dynamic))
1166 /* Make a new one */
1167 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
1170 ast_mutex_init(&cnf->playlock);
1171 ast_mutex_init(&cnf->listenlock);
1172 cnf->recordthread = AST_PTHREADT_NULL;
1173 ast_mutex_init(&cnf->recordthreadlock);
1174 cnf->announcethread = AST_PTHREADT_NULL;
1175 ast_mutex_init(&cnf->announcethreadlock);
1176 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
1177 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
1178 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
1179 ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
1181 /* Setup a new dahdi conference */
1183 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1184 cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
1185 if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
1186 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
1194 cnf->dahdiconf = dahdic.confno;
1196 /* Setup a new channel for playback of audio files */
1197 cnf->chan = ast_request("DAHDI", AST_FORMAT_SLINEAR, chan, "pseudo", NULL);
1199 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
1200 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
1202 dahdic.confno = cnf->dahdiconf;
1203 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1204 if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &dahdic)) {
1205 ast_log(LOG_WARNING, "Error setting conference\n");
1207 ast_hangup(cnf->chan);
1217 /* Fill the conference struct */
1218 cnf->start = time(NULL);
1219 cnf->maxusers = 0x7fffffff;
1220 cnf->isdynamic = dynamic ? 1 : 0;
1221 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
1222 AST_LIST_INSERT_HEAD(&confs, cnf, list);
1224 /* Reserve conference number in map */
1225 if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1226 conf_map[confno_int] = 1;
1230 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
1232 AST_LIST_UNLOCK(&confs);
1237 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
1239 static const char * const cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
1241 int len = strlen(word);
1243 struct ast_conference *cnf = NULL;
1244 struct ast_conf_user *usr = NULL;
1245 char *confno = NULL;
1246 char usrno[50] = "";
1247 char *myline, *ret = NULL;
1249 if (pos == 1) { /* Command */
1250 return ast_cli_complete(word, cmds, state);
1251 } else if (pos == 2) { /* Conference Number */
1252 AST_LIST_LOCK(&confs);
1253 AST_LIST_TRAVERSE(&confs, cnf, list) {
1254 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
1259 ret = ast_strdup(ret); /* dup before releasing the lock */
1260 AST_LIST_UNLOCK(&confs);
1262 } else if (pos == 3) {
1263 /* User Number || Conf Command option*/
1264 if (strstr(line, "mute") || strstr(line, "kick")) {
1265 if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
1266 return ast_strdup("all");
1268 AST_LIST_LOCK(&confs);
1270 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
1271 myline = ast_strdupa(line);
1272 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
1273 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
1277 AST_LIST_TRAVERSE(&confs, cnf, list) {
1278 if (!strcmp(confno, cnf->confno))
1283 /* Search for the user */
1284 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
1285 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
1286 if (!strncasecmp(word, usrno, len) && ++which > state)
1290 AST_LIST_UNLOCK(&confs);
1291 return usr ? ast_strdup(usrno) : NULL;
1298 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1300 /* Process the command */
1301 struct ast_conf_user *user;
1302 struct ast_conference *cnf;
1304 int i = 0, total = 0;
1306 struct ast_str *cmdline = NULL;
1307 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
1308 #define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
1312 e->command = "meetme list [concise]";
1314 "Usage: meetme list [concise] <confno> \n"
1315 " List all or a specific conference.\n";
1318 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1321 /* Check for length so no buffer will overflow... */
1322 for (i = 0; i < a->argc; i++) {
1323 if (strlen(a->argv[i]) > 100)
1324 ast_cli(a->fd, "Invalid Arguments.\n");
1327 /* Max confno length */
1328 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1332 if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], "concise"))) {
1333 /* List all the conferences */
1334 int concise = (a->argc == 3 && !strcasecmp(a->argv[2], "concise"));
1336 AST_LIST_LOCK(&confs);
1337 if (AST_LIST_EMPTY(&confs)) {
1339 ast_cli(a->fd, "No active MeetMe conferences.\n");
1341 AST_LIST_UNLOCK(&confs);
1346 ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
1348 AST_LIST_TRAVERSE(&confs, cnf, list) {
1349 if (cnf->markedusers == 0) {
1350 ast_str_set(&cmdline, 0, "N/A ");
1352 ast_str_set(&cmdline, 0, "%4.4d", cnf->markedusers);
1354 hr = (now - cnf->start) / 3600;
1355 min = ((now - cnf->start) % 3600) / 60;
1356 sec = (now - cnf->start) % 60;
1358 ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users, ast_str_buffer(cmdline), hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
1360 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1369 total += cnf->users;
1371 AST_LIST_UNLOCK(&confs);
1373 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1377 } else if (strcmp(a->argv[1], "list") == 0) {
1378 int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
1379 /* List all the users in a conference */
1380 if (AST_LIST_EMPTY(&confs)) {
1382 ast_cli(a->fd, "No active MeetMe conferences.\n");
1387 /* Find the right conference */
1388 AST_LIST_LOCK(&confs);
1389 AST_LIST_TRAVERSE(&confs, cnf, list) {
1390 if (strcmp(cnf->confno, a->argv[2]) == 0) {
1396 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
1397 AST_LIST_UNLOCK(&confs);
1401 /* Show all the users */
1403 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
1404 hr = (now - user->jointime) / 3600;
1405 min = ((now - user->jointime) % 3600) / 60;
1406 sec = (now - user->jointime) % 60;
1408 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1410 S_OR(user->chan->cid.cid_num, "<unknown>"),
1411 S_OR(user->chan->cid.cid_name, "<no name>"),
1413 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "(Admin)" : "",
1414 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "(Listen only)" : "",
1415 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1416 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1417 istalking(user->talking), hr, min, sec);
1419 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1421 S_OR(user->chan->cid.cid_num, ""),
1422 S_OR(user->chan->cid.cid_name, ""),
1424 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "1" : "",
1425 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "1" : "",
1426 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1427 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1428 user->talking, hr, min, sec);
1432 ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
1434 AST_LIST_UNLOCK(&confs);
1440 return CLI_SHOWUSAGE;
1443 ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
1445 admin_exec(NULL, ast_str_buffer(cmdline));
1452 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1454 /* Process the command */
1455 struct ast_str *cmdline = NULL;
1460 e->command = "meetme {lock|unlock|mute|unmute|kick}";
1462 "Usage: meetme (un)lock|(un)mute|kick <confno> <usernumber>\n"
1463 " Executes a command for the conference or on a conferee\n";
1466 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1470 ast_cli(a->fd, "Invalid Arguments.\n");
1471 /* Check for length so no buffer will overflow... */
1472 for (i = 0; i < a->argc; i++) {
1473 if (strlen(a->argv[i]) > 100)
1474 ast_cli(a->fd, "Invalid Arguments.\n");
1477 /* Max confno length */
1478 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1484 return CLI_SHOWUSAGE;
1487 ast_str_set(&cmdline, 0, "%s", a->argv[2]); /* Argv 2: conference number */
1488 if (strstr(a->argv[1], "lock")) {
1489 if (strcmp(a->argv[1], "lock") == 0) {
1491 ast_str_append(&cmdline, 0, ",L");
1494 ast_str_append(&cmdline, 0, ",l");
1496 } else if (strstr(a->argv[1], "mute")) {
1499 return CLI_SHOWUSAGE;
1501 if (strcmp(a->argv[1], "mute") == 0) {
1503 if (strcmp(a->argv[3], "all") == 0) {
1504 ast_str_append(&cmdline, 0, ",N");
1506 ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);
1510 if (strcmp(a->argv[3], "all") == 0) {
1511 ast_str_append(&cmdline, 0, ",n");
1513 ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
1516 } else if (strcmp(a->argv[1], "kick") == 0) {
1519 return CLI_SHOWUSAGE;
1521 if (strcmp(a->argv[3], "all") == 0) {
1523 ast_str_append(&cmdline, 0, ",K");
1525 /* Kick a single user */
1526 ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
1530 return CLI_SHOWUSAGE;
1533 ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
1535 admin_exec(NULL, ast_str_buffer(cmdline));
1541 static const char *sla_hold_str(unsigned int hold_access)
1543 const char *hold = "Unknown";
1545 switch (hold_access) {
1549 case SLA_HOLD_PRIVATE:
1558 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1560 const struct sla_trunk *trunk;
1564 e->command = "sla show trunks";
1566 "Usage: sla show trunks\n"
1567 " This will list all trunks defined in sla.conf\n";
1574 "=============================================================\n"
1575 "=== Configured SLA Trunks ===================================\n"
1576 "=============================================================\n"
1578 AST_RWLIST_RDLOCK(&sla_trunks);
1579 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1580 struct sla_station_ref *station_ref;
1581 char ring_timeout[16] = "(none)";
1582 if (trunk->ring_timeout)
1583 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1584 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1585 "=== Trunk Name: %s\n"
1586 "=== ==> Device: %s\n"
1587 "=== ==> AutoContext: %s\n"
1588 "=== ==> RingTimeout: %s\n"
1589 "=== ==> BargeAllowed: %s\n"
1590 "=== ==> HoldAccess: %s\n"
1591 "=== ==> Stations ...\n",
1592 trunk->name, trunk->device,
1593 S_OR(trunk->autocontext, "(none)"),
1595 trunk->barge_disabled ? "No" : "Yes",
1596 sla_hold_str(trunk->hold_access));
1597 AST_RWLIST_RDLOCK(&sla_stations);
1598 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1599 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
1600 AST_RWLIST_UNLOCK(&sla_stations);
1601 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
1603 AST_RWLIST_UNLOCK(&sla_trunks);
1604 ast_cli(a->fd, "=============================================================\n\n");
1609 static const char *trunkstate2str(enum sla_trunk_state state)
1611 #define S(e) case e: return # e;
1613 S(SLA_TRUNK_STATE_IDLE)
1614 S(SLA_TRUNK_STATE_RINGING)
1615 S(SLA_TRUNK_STATE_UP)
1616 S(SLA_TRUNK_STATE_ONHOLD)
1617 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1619 return "Uknown State";
1623 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1625 const struct sla_station *station;
1629 e->command = "sla show stations";
1631 "Usage: sla show stations\n"
1632 " This will list all stations defined in sla.conf\n";
1639 "=============================================================\n"
1640 "=== Configured SLA Stations =================================\n"
1641 "=============================================================\n"
1643 AST_RWLIST_RDLOCK(&sla_stations);
1644 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1645 struct sla_trunk_ref *trunk_ref;
1646 char ring_timeout[16] = "(none)";
1647 char ring_delay[16] = "(none)";
1648 if (station->ring_timeout) {
1649 snprintf(ring_timeout, sizeof(ring_timeout),
1650 "%u", station->ring_timeout);
1652 if (station->ring_delay) {
1653 snprintf(ring_delay, sizeof(ring_delay),
1654 "%u", station->ring_delay);
1656 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1657 "=== Station Name: %s\n"
1658 "=== ==> Device: %s\n"
1659 "=== ==> AutoContext: %s\n"
1660 "=== ==> RingTimeout: %s\n"
1661 "=== ==> RingDelay: %s\n"
1662 "=== ==> HoldAccess: %s\n"
1663 "=== ==> Trunks ...\n",
1664 station->name, station->device,
1665 S_OR(station->autocontext, "(none)"),
1666 ring_timeout, ring_delay,
1667 sla_hold_str(station->hold_access));
1668 AST_RWLIST_RDLOCK(&sla_trunks);
1669 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1670 if (trunk_ref->ring_timeout) {
1671 snprintf(ring_timeout, sizeof(ring_timeout),
1672 "%u", trunk_ref->ring_timeout);
1674 strcpy(ring_timeout, "(none)");
1675 if (trunk_ref->ring_delay) {
1676 snprintf(ring_delay, sizeof(ring_delay),
1677 "%u", trunk_ref->ring_delay);
1679 strcpy(ring_delay, "(none)");
1680 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
1681 "=== ==> State: %s\n"
1682 "=== ==> RingTimeout: %s\n"
1683 "=== ==> RingDelay: %s\n",
1684 trunk_ref->trunk->name,
1685 trunkstate2str(trunk_ref->state),
1686 ring_timeout, ring_delay);
1688 AST_RWLIST_UNLOCK(&sla_trunks);
1689 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1692 AST_RWLIST_UNLOCK(&sla_stations);
1693 ast_cli(a->fd, "============================================================\n"
1699 static struct ast_cli_entry cli_meetme[] = {
1700 AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
1701 AST_CLI_DEFINE(meetme_show_cmd, "List all or one conference"),
1702 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
1703 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
1706 static void conf_flush(int fd, struct ast_channel *chan)
1710 /* read any frames that may be waiting on the channel
1714 struct ast_frame *f;
1716 /* when no frames are available, this will wait
1717 for 1 millisecond maximum
1719 while (ast_waitfor(chan, 1)) {
1723 else /* channel was hung up or something else happened */
1728 /* flush any data sitting in the pseudo channel */
1729 x = DAHDI_FLUSH_ALL;
1730 if (ioctl(fd, DAHDI_FLUSH, &x))
1731 ast_log(LOG_WARNING, "Error flushing channel\n");
1735 /*! \brief Remove the conference from the list and free it.
1737 We assume that this was called while holding conflock. */
1738 static int conf_free(struct ast_conference *conf)
1741 struct announce_listitem *item;
1743 AST_LIST_REMOVE(&confs, conf, list);
1744 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1746 if (conf->recording == MEETME_RECORD_ACTIVE) {
1747 conf->recording = MEETME_RECORD_TERMINATE;
1748 AST_LIST_UNLOCK(&confs);
1751 AST_LIST_LOCK(&confs);
1752 if (conf->recording == MEETME_RECORD_OFF)
1754 AST_LIST_UNLOCK(&confs);
1758 for (x = 0; x < AST_FRAME_BITS; x++) {
1759 if (conf->transframe[x])
1760 ast_frfree(conf->transframe[x]);
1761 if (conf->transpath[x])
1762 ast_translator_free_path(conf->transpath[x]);
1764 if (conf->announcethread != AST_PTHREADT_NULL) {
1765 ast_mutex_lock(&conf->announcelistlock);
1766 conf->announcethread_stop = 1;
1767 ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
1768 ast_cond_signal(&conf->announcelist_addition);
1769 ast_mutex_unlock(&conf->announcelistlock);
1770 pthread_join(conf->announcethread, NULL);
1772 while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
1773 ast_filedelete(item->namerecloc, NULL);
1776 ast_mutex_destroy(&conf->announcelistlock);
1778 if (conf->origframe)
1779 ast_frfree(conf->origframe);
1781 ast_hangup(conf->lchan);
1783 ast_hangup(conf->chan);
1786 if (conf->recordingfilename) {
1787 ast_free(conf->recordingfilename);
1789 if (conf->recordingformat) {
1790 ast_free(conf->recordingformat);
1792 ast_mutex_destroy(&conf->playlock);
1793 ast_mutex_destroy(&conf->listenlock);
1794 ast_mutex_destroy(&conf->recordthreadlock);
1795 ast_mutex_destroy(&conf->announcethreadlock);
1801 static void conf_queue_dtmf(const struct ast_conference *conf,
1802 const struct ast_conf_user *sender, struct ast_frame *f)
1804 struct ast_conf_user *user;
1806 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1809 if (ast_write(user->chan, f) < 0)
1810 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1814 static void sla_queue_event_full(enum sla_event_type type,
1815 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1817 struct sla_event *event;
1819 if (sla.thread == AST_PTHREADT_NULL) {
1823 if (!(event = ast_calloc(1, sizeof(*event))))
1827 event->trunk_ref = trunk_ref;
1828 event->station = station;
1831 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1835 ast_mutex_lock(&sla.lock);
1836 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1837 ast_cond_signal(&sla.cond);
1838 ast_mutex_unlock(&sla.lock);
1841 static void sla_queue_event_nolock(enum sla_event_type type)
1843 sla_queue_event_full(type, NULL, NULL, 0);
1846 static void sla_queue_event(enum sla_event_type type)
1848 sla_queue_event_full(type, NULL, NULL, 1);
1851 /*! \brief Queue a SLA event from the conference */
1852 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1853 struct ast_conference *conf)
1855 struct sla_station *station;
1856 struct sla_trunk_ref *trunk_ref = NULL;
1859 trunk_name = ast_strdupa(conf->confno);
1860 strsep(&trunk_name, "_");
1861 if (ast_strlen_zero(trunk_name)) {
1862 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1866 AST_RWLIST_RDLOCK(&sla_stations);
1867 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1868 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1869 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1875 AST_RWLIST_UNLOCK(&sla_stations);
1878 ast_debug(1, "Trunk not found for event!\n");
1882 sla_queue_event_full(type, trunk_ref, station, 1);
1885 /*! \brief Decrement reference counts, as incremented by find_conf() */
1886 static int dispose_conf(struct ast_conference *conf)
1891 AST_LIST_LOCK(&confs);
1892 if (ast_atomic_dec_and_test(&conf->refcount)) {
1893 /* Take the conference room number out of an inuse state */
1894 if ((sscanf(conf->confno, "%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
1895 conf_map[confno_int] = 0;
1900 AST_LIST_UNLOCK(&confs);
1905 static int rt_extend_conf(const char *confno)
1907 char currenttime[32];
1911 struct ast_variable *var, *orig_var;
1920 ast_localtime(&now, &tm, NULL);
1921 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1923 var = ast_load_realtime("meetme", "confno",
1924 confno, "startTime<= ", currenttime,
1925 "endtime>= ", currenttime, NULL);
1929 /* Identify the specific RealTime conference */
1931 if (!strcasecmp(var->name, "bookid")) {
1932 ast_copy_string(bookid, var->value, sizeof(bookid));
1934 if (!strcasecmp(var->name, "endtime")) {
1935 ast_copy_string(endtime, var->value, sizeof(endtime));
1940 ast_variables_destroy(orig_var);
1942 ast_strptime(endtime, DATE_FORMAT, &tm);
1943 now = ast_mktime(&tm, NULL);
1945 now.tv_sec += extendby;
1947 ast_localtime(&now, &tm, NULL);
1948 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1949 strcat(currenttime, "0"); /* Seconds needs to be 00 */
1951 var = ast_load_realtime("meetme", "confno",
1952 confno, "startTime<= ", currenttime,
1953 "endtime>= ", currenttime, NULL);
1955 /* If there is no conflict with extending the conference, update the DB */
1957 ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
1958 ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
1963 ast_variables_destroy(var);
1967 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
1971 ast_channel_lock(chan);
1972 original_moh = ast_strdupa(chan->musicclass);
1973 ast_string_field_set(chan, musicclass, musicclass);
1974 ast_channel_unlock(chan);
1976 ast_moh_start(chan, original_moh, NULL);
1978 ast_channel_lock(chan);
1979 ast_string_field_set(chan, musicclass, original_moh);
1980 ast_channel_unlock(chan);
1983 static const char *get_announce_filename(enum announcetypes type)
1987 return "conf-hasleft";
1990 return "conf-hasjoin";
1997 static void *announce_thread(void *data)
1999 struct announce_listitem *current;
2000 struct ast_conference *conf = data;
2002 char filename[PATH_MAX] = "";
2003 AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
2004 AST_LIST_HEAD_INIT_NOLOCK(&local_list);
2006 while (!conf->announcethread_stop) {
2007 ast_mutex_lock(&conf->announcelistlock);
2008 if (conf->announcethread_stop) {
2009 ast_mutex_unlock(&conf->announcelistlock);
2012 if (AST_LIST_EMPTY(&conf->announcelist))
2013 ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
2015 AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
2016 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2018 ast_mutex_unlock(&conf->announcelistlock);
2019 if (conf->announcethread_stop) {
2023 for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
2024 ast_log(LOG_DEBUG, "About to play %s\n", current->namerecloc);
2025 if (!ast_fileexists(current->namerecloc, NULL, NULL))
2027 if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
2028 if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
2029 res = ast_waitstream(current->confchan, "");
2031 ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
2032 if (!ast_streamfile(current->confchan, filename, current->language))
2033 ast_waitstream(current->confchan, "");
2036 if (current->announcetype == CONF_HASLEFT) {
2037 ast_filedelete(current->namerecloc, NULL);
2042 /* thread marked to stop, clean up */
2043 while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
2044 ast_filedelete(current->namerecloc, NULL);
2045 ao2_ref(current, -1);
2050 static int can_write(struct ast_channel *chan, struct ast_flags64 *confflags)
2052 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
2056 return (chan->_state == AST_STATE_UP);
2059 static void send_talking_event(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking)
2061 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalking",
2067 chan->name, chan->uniqueid, conf->confno, user->user_no, talking ? "on" : "off");
2070 static void set_user_talking(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor)
2072 int last_talking = user->talking;
2073 if (last_talking == talking)
2076 user->talking = talking;
2079 /* Check if talking state changed. Take care of -1 which means unmonitored */
2080 int was_talking = (last_talking > 0);
2081 int now_talking = (talking > 0);
2082 if (was_talking != now_talking) {
2083 send_talking_event(chan, conf, user, now_talking);
2088 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struct ast_flags64 *confflags, char *optargs[])
2090 struct ast_conf_user *user = NULL;
2091 struct ast_conf_user *usr = NULL;
2093 struct dahdi_confinfo dahdic, dahdic_empty;
2094 struct ast_frame *f;
2095 struct ast_channel *c;
2096 struct ast_frame fr;
2103 int musiconhold = 0, mohtempstopped = 0;
2106 int currentmarked = 0;
2109 int menu_active = 0;
2110 int menu8_active = 0;
2111 int talkreq_manager = 0;
2112 int using_pseudo = 0;
2117 int announcement_played = 0;
2119 struct ast_dsp *dsp = NULL;
2120 struct ast_app *agi_app;
2121 char *agifile, *mod_speex;
2122 const char *agifiledefault = "conf-background.agi", *tmpvar;
2123 char meetmesecs[30] = "";
2124 char exitcontext[AST_MAX_CONTEXT] = "";
2125 char recordingtmp[AST_MAX_EXTENSION] = "";
2126 char members[10] = "";
2127 int dtmf, opt_waitmarked_timeout = 0;
2129 struct dahdi_bufferinfo bi;
2130 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
2131 char *buf = __buf + AST_FRIENDLY_OFFSET;
2132 char *exitkeys = NULL;
2133 unsigned int calldurationlimit = 0;
2135 long play_warning = 0;
2136 long warning_freq = 0;
2137 const char *warning_sound = NULL;
2138 const char *end_sound = NULL;
2140 long time_left_ms = 0;
2141 struct timeval nexteventts = { 0, };
2143 int setusercount = 0;
2144 int confsilence = 0, totalsilence = 0;
2146 if (!(user = ast_calloc(1, sizeof(*user))))
2149 /* Possible timeout waiting for marked user */
2150 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
2151 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
2152 (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
2153 (opt_waitmarked_timeout > 0)) {
2154 timeout = time(NULL) + opt_waitmarked_timeout;
2157 if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
2158 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
2159 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
2162 if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
2163 char *limit_str, *warning_str, *warnfreq_str;
2166 parse = optargs[OPT_ARG_DURATION_LIMIT];
2167 limit_str = strsep(&parse, ":");
2168 warning_str = strsep(&parse, ":");
2169 warnfreq_str = parse;
2171 timelimit = atol(limit_str);
2173 play_warning = atol(warning_str);
2175 warning_freq = atol(warnfreq_str);
2178 timelimit = play_warning = warning_freq = 0;
2179 warning_sound = NULL;
2180 } else if (play_warning > timelimit) {
2181 if (!warning_freq) {
2184 while (play_warning > timelimit)
2185 play_warning -= warning_freq;
2186 if (play_warning < 1)
2187 play_warning = warning_freq = 0;
2191 ast_channel_lock(chan);
2192 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
2193 var = ast_strdupa(var);
2195 ast_channel_unlock(chan);
2197 warning_sound = var ? var : "timeleft";
2199 ast_channel_lock(chan);
2200 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
2201 var = ast_strdupa(var);
2203 ast_channel_unlock(chan);
2205 end_sound = var ? var : NULL;
2207 /* undo effect of S(x) in case they are both used */
2208 calldurationlimit = 0;
2209 /* more efficient do it like S(x) does since no advanced opts */
2210 if (!play_warning && !end_sound && timelimit) {
2211 calldurationlimit = timelimit / 1000;
2212 timelimit = play_warning = warning_freq = 0;
2214 ast_debug(2, "Limit Data for this call:\n");
2215 ast_debug(2, "- timelimit = %ld\n", timelimit);
2216 ast_debug(2, "- play_warning = %ld\n", play_warning);
2217 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
2218 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
2219 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
2224 if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
2225 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
2226 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
2228 exitkeys = ast_strdupa("#"); /* Default */
2231 if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
2232 if (!conf->recordingfilename) {
2234 ast_channel_lock(chan);
2235 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
2236 conf->recordingfilename = ast_strdup(var);
2238 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
2239 conf->recordingformat = ast_strdup(var);
2241 ast_channel_unlock(chan);
2242 if (!conf->recordingfilename) {
2243 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
2244 conf->recordingfilename = ast_strdup(recordingtmp);
2246 if (!conf->recordingformat) {
2247 conf->recordingformat = ast_strdup("wav");
2249 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
2250 conf->confno, conf->recordingfilename, conf->recordingformat);
2254 ast_mutex_lock(&conf->recordthreadlock);
2255 if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
2256 ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, chan, "pseudo", NULL)))) {
2257 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
2258 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
2260 dahdic.confno = conf->dahdiconf;
2261 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
2262 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
2263 ast_log(LOG_WARNING, "Error starting listen channel\n");
2264 ast_hangup(conf->lchan);
2267 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
2270 ast_mutex_unlock(&conf->recordthreadlock);
2272 ast_mutex_lock(&conf->announcethreadlock);
2273 if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
2274 (ast_test_flag64(confflags, CONFFLAG_INTROUSER) || ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))) {
2275 ast_mutex_init(&conf->announcelistlock);
2276 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2277 ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
2279 ast_mutex_unlock(&conf->announcethreadlock);
2281 time(&user->jointime);
2283 user->timelimit = timelimit;
2284 user->play_warning = play_warning;
2285 user->warning_freq = warning_freq;
2286 user->warning_sound = warning_sound;
2287 user->end_sound = end_sound;
2289 if (calldurationlimit > 0) {
2290 time(&user->kicktime);
2291 user->kicktime = user->kicktime + calldurationlimit;
2294 if (ast_tvzero(user->start_time))
2295 user->start_time = ast_tvnow();
2296 time_left_ms = user->timelimit;
2298 if (user->timelimit) {
2299 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2300 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
2303 if (conf->locked && (!ast_test_flag64(confflags, CONFFLAG_ADMIN))) {
2304 /* Sorry, but this conference is locked! */
2305 if (!ast_streamfile(chan, "conf-locked", chan->language))
2306 ast_waitstream(chan, "");
2310 ast_mutex_lock(&conf->playlock);
2312 if (AST_LIST_EMPTY(&conf->userlist))
2315 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
2317 if (rt_schedule && conf->maxusers)
2318 if (conf->users >= conf->maxusers) {
2319 /* Sorry, but this confernce has reached the participant limit! */
2320 if (!ast_streamfile(chan, "conf-full", chan->language))
2321 ast_waitstream(chan, "");
2322 ast_mutex_unlock(&conf->playlock);
2327 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
2330 user->userflags = *confflags;
2331 user->adminflags = ast_test_flag64(confflags, CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
2332 user->adminflags |= (conf->gmuted) ? ADMINFLAG_MUTED : 0;
2335 ast_mutex_unlock(&conf->playlock);
2337 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
2338 ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))) {
2339 char destdir[PATH_MAX];
2341 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
2343 if (ast_mkdir(destdir, 0777) != 0) {
2344 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
2348 snprintf(user->namerecloc, sizeof(user->namerecloc),
2349 "%s/meetme-username-%s-%d", destdir,
2350 conf->confno, user->user_no);
2351 if (ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))
2352 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
2354 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
2359 ast_mutex_lock(&conf->playlock);
2361 if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER))
2362 conf->markedusers++;
2364 if (rt_log_members) {
2366 snprintf(members, sizeof(members), "%d", conf->users);
2367 ast_realtime_require_field("meetme",
2368 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
2369 "members", RQ_UINTEGER1, strlen(members),
2371 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2375 /* This device changed state now - if this is the first user */
2376 if (conf->users == 1)
2377 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
2379 ast_mutex_unlock(&conf->playlock);
2381 /* return the unique ID of the conference */
2382 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
2384 if (ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT)) {
2385 ast_channel_lock(chan);
2386 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
2387 ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
2388 } else if (!ast_strlen_zero(chan->macrocontext)) {
2389 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
2391 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
2393 ast_channel_unlock(chan);
2396 /* Play an arbitrary intro message */
2397 if (ast_test_flag64(confflags, CONFFLAG_INTROMSG) &&
2398 !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
2399 if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], chan->language)) {
2400 ast_waitstream(chan, "");
2404 if (!ast_test_flag64(confflags, (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
2405 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED))
2406 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
2407 ast_waitstream(chan, "");
2408 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && conf->markedusers == 0)
2409 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
2410 ast_waitstream(chan, "");
2413 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_ANNOUNCEUSERCOUNT) &&
2415 int keepplaying = 1;
2417 if (conf->users == 2) {
2418 if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
2419 res = ast_waitstream(chan, AST_DIGIT_ANY);
2420 ast_stopstream(chan);
2427 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
2428 res = ast_waitstream(chan, AST_DIGIT_ANY);
2429 ast_stopstream(chan);
2436 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2442 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
2443 res = ast_waitstream(chan, AST_DIGIT_ANY);
2444 ast_stopstream(chan);
2453 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
2454 /* We're leaving this alone until the state gets changed to up */
2455 ast_indicate(chan, -1);
2458 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
2459 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
2463 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
2464 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
2468 /* Reduce background noise from each participant */
2469 if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
2470 ast_free(mod_speex);
2471 ast_func_write(chan, "DENOISE(rx)", "on");
2474 retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
2475 user->dahdichannel = !retrydahdi;
2478 origfd = chan->fds[0];
2480 /* open pseudo in non-blocking mode */
2481 fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
2483 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
2487 /* Setup buffering information */
2488 memset(&bi, 0, sizeof(bi));
2489 bi.bufsize = CONF_SIZE / 2;
2490 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
2491 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
2492 bi.numbufs = audio_buffers;
2493 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
2494 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
2499 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
2500 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
2506 /* XXX Make sure we're not running on a pseudo channel XXX */
2510 memset(&dahdic, 0, sizeof(dahdic));
2511 memset(&dahdic_empty, 0, sizeof(dahdic_empty));
2512 /* Check to see if we're in a conference... */
2514 if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
2515 ast_log(LOG_WARNING, "Error getting conference\n");
2519 if (dahdic.confmode) {
2520 /* Whoa, already in a conference... Retry... */
2522 ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
2527 memset(&dahdic, 0, sizeof(dahdic));
2528 /* Add us to the conference */
2530 dahdic.confno = conf->dahdiconf;
2532 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
2533 ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
2534 struct announce_listitem *item;
2535 if (!(item = ao2_alloc(sizeof(*item), NULL)))
2537 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
2538 ast_copy_string(item->language, chan->language, sizeof(item->language));
2539 item->confchan = conf->chan;
2540 item->confusers = conf->users;
2541 item->announcetype = CONF_HASJOIN;
2542 ast_mutex_lock(&conf->announcelistlock);
2543 ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
2544 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
2545 ast_cond_signal(&conf->announcelist_addition);
2546 ast_mutex_unlock(&conf->announcelistlock);
2548 while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
2554 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED && !conf->markedusers))
2555 dahdic.confmode = DAHDI_CONF_CONF;
2556 else if (ast_test_flag64(confflags, CONFFLAG_MONITOR))
2557 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2558 else if (ast_test_flag64(confflags, CONFFLAG_TALKER))
2559 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2561 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2563 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2564 ast_log(LOG_WARNING, "Error setting conference\n");
2568 ast_debug(1, "Placed channel %s in DAHDI conf %d\n", chan->name, conf->dahdiconf);
2571 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeJoin",
2576 "CallerIDnum: %s\r\n"
2577 "CallerIDname: %s\r\n",
2578 chan->name, chan->uniqueid, conf->confno,
2580 S_OR(user->chan->cid.cid_num, "<unknown>"),
2581 S_OR(user->chan->cid.cid_name, "<unknown>")
2586 if (!firstpass && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
2587 !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
2589 if (!ast_test_flag64(confflags, CONFFLAG_QUIET))
2590 if (!ast_test_flag64(confflags, CONFFLAG_WAITMARKED) || (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
2591 (conf->markedusers >= 1))) {
2592 conf_play(chan, conf, ENTER);
2596 conf_flush(fd, chan);
2598 if (!(dsp = ast_dsp_new())) {
2599 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
2603 if (ast_test_flag64(confflags, CONFFLAG_AGI)) {
2604 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
2605 or use default filename of conf-background.agi */
2607 ast_channel_lock(chan);
2608 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
2609 agifile = ast_strdupa(tmpvar);
2611 agifile = ast_strdupa(agifiledefault);
2613 ast_channel_unlock(chan);
2615 if (user->dahdichannel) {
2616 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
2618 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2620 /* Find a pointer to the agi app and execute the script */
2621 agi_app = pbx_findapp("agi");
2623 ret = pbx_exec(chan, agi_app, agifile);
2625 ast_log(LOG_WARNING, "Could not find application (agi)\n");
2628 if (user->dahdichannel) {
2629 /* Remove CONFMUTE mode on DAHDI channel */
2631 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2634 if (user->dahdichannel && ast_test_flag64(confflags, CONFFLAG_STARMENU)) {
2635 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
2637 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2640 int menu_was_active = 0;
2646 if (rt_schedule && conf->endtime) {
2647 char currenttime[32];
2648 long localendtime = 0;
2651 struct ast_variable *var, *origvar;
2654 if (now.tv_sec % 60 == 0) {
2656 ast_localtime(&now, &tm, NULL);
2657 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2658 var = origvar = ast_load_realtime("meetme", "confno",
2659 conf->confno, "starttime <=", currenttime,
2660 "endtime >=", currenttime, NULL);
2662 for ( ; var; var = var->next) {
2663 if (!strcasecmp(var->name, "endtime")) {
2664 struct ast_tm endtime_tm;
2665 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
2666 tmp = ast_mktime(&endtime_tm, NULL);
2667 localendtime = tmp.tv_sec;
2670 ast_variables_destroy(origvar);
2672 /* A conference can be extended from the
2673 Admin/User menu or by an external source */
2674 if (localendtime > conf->endtime){
2675 conf->endtime = localendtime;
2679 if (conf->endtime && (now.tv_sec >= conf->endtime)) {
2680 ast_verbose("Quitting time...\n");
2684 if (!announcement_played && conf->endalert) {
2685 if (now.tv_sec + conf->endalert >= conf->endtime) {
2686 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
2687 ast_waitstream(chan, "");
2688 ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", chan->language);
2689 if (!ast_streamfile(chan, "minutes", chan->language))
2690 ast_waitstream(chan, "");
2691 announcement_played = 1;
2696 announcement_played = 0;
2706 if (user->kicktime && (user->kicktime <= now.tv_sec)) {
2711 if (user->timelimit) {
2712 int minutes = 0, seconds = 0, remain = 0;
2714 to = ast_tvdiff_ms(nexteventts, now);
2718 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
2719 if (time_left_ms < to) {
2723 if (time_left_ms <= 0) {
2724 if (user->end_sound) {
2725 res = ast_streamfile(chan, user->end_sound, chan->language);
2726 res = ast_waitstream(chan, "");
2732 if (time_left_ms >= 5000) {
2734 remain = (time_left_ms + 500) / 1000;
2735 if (remain / 60 >= 1) {
2736 minutes = remain / 60;
2737 seconds = remain % 60;
2742 /* force the time left to round up if appropriate */
2743 if (user->warning_sound && user->play_warning) {
2744 if (!strcmp(user->warning_sound, "timeleft")) {
2746 res = ast_streamfile(chan, "vm-youhave", chan->language);
2747 res = ast_waitstream(chan, "");
2749 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
2750 res = ast_streamfile(chan, "queue-minutes", chan->language);
2751 res = ast_waitstream(chan, "");
2754 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
2755 res = ast_streamfile(chan, "queue-seconds", chan->language);
2756 res = ast_waitstream(chan, "");
2759 res = ast_streamfile(chan, user->warning_sound, chan->language);
2760 res = ast_waitstream(chan, "");
2764 if (user->warning_freq) {
2765 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
2767 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2773 if (timeout && now.tv_sec >= timeout) {
2777 /* if we have just exited from the menu, and the user had a channel-driver
2778 volume adjustment, restore it
2780 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual) {
2781 set_talk_volume(user, user->listen.desired);
2784 menu_was_active = menu_active;
2786 currentmarked = conf->markedusers;
2787 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
2788 ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
2789 ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
2791 if (currentmarked == 1 && conf->users > 1) {
2792 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2793 if (conf->users - 1 == 1) {
2794 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language)) {
2795 ast_waitstream(chan, "");
2798 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language)) {
2799 ast_waitstream(chan, "");
2803 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
2804 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
2805 ast_waitstream(chan, "");
2810 /* Update the struct with the actual confflags */
2811 user->userflags = *confflags;
2813 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
2814 if (currentmarked == 0) {
2815 if (lastmarked != 0) {
2816 if (!ast_test_flag64(confflags, CONFFLAG_QUIET)) {
2817 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language)) {
2818 ast_waitstream(chan, "");
2821 if (ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
2822 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
2827 dahdic.confmode = DAHDI_CONF_CONF;
2828 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2829 ast_log(LOG_WARNING, "Error setting conference\n");
2835 if (!musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
2836 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2839 } else if (currentmarked >= 1 && lastmarked == 0) {
2840 /* Marked user entered, so cancel timeout */
2842 if (ast_test_flag64(confflags, CONFFLAG_MONITOR)) {
2843 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2844 } else if (ast_test_flag64(confflags, CONFFLAG_TALKER)) {
2845 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2847 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2849 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2850 ast_log(LOG_WARNING, "Error setting conference\n");
2854 if (musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
2858 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
2859 !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
2860 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language)) {
2861 ast_waitstream(chan, "");
2863 conf_play(chan, conf, ENTER);
2868 /* trying to add moh for single person conf */
2869 if (ast_test_flag64(confflags, CONFFLAG_MOH) && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
2870 if (conf->users == 1) {
2872 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2883 /* Leave if the last marked user left */
2884 if (currentmarked == 0 && lastmarked != 0 && ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
2885 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
2893 /* Check if my modes have changed */
2895 /* If I should be muted but am still talker, mute me */
2896 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
2897 dahdic.confmode ^= DAHDI_CONF_TALKER;
2898 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2899 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2904 /* Indicate user is not talking anymore - change him to unmonitored state */
2905 if (ast_test_flag64(confflags, (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER))) {
2906 set_user_talking(chan, conf, user, -1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
2909 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
2915 chan->name, chan->uniqueid, conf->confno, user->user_no);
2918 /* If I should be un-muted but am not talker, un-mute me */
2919 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
2920 dahdic.confmode |= DAHDI_CONF_TALKER;
2921 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2922 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2927 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
2933 chan->name, chan->uniqueid, conf->confno, user->user_no);
2936 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2937 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
2938 talkreq_manager = 1;
2940 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
2946 chan->name, chan->uniqueid, conf->confno, user->user_no);
2950 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2951 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
2952 talkreq_manager = 0;
2953 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
2959 chan->name, chan->uniqueid, conf->confno, user->user_no);
2962 /* If I have been kicked, exit the conference */
2963 if (user->adminflags & ADMINFLAG_KICKME) {
2964 /* You have been kicked. */
2965 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
2966 !ast_streamfile(chan, "conf-kicked", chan->language)) {
2967 ast_waitstream(chan, "");
2973 /* Perform an extra hangup check just in case */
2974 if (ast_check_hangup(chan)) {
2978 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
2981 char dtmfstr[2] = "";
2983 if (c->fds[0] != origfd || (user->dahdichannel && (c->audiohooks || c->monitor))) {
2985 /* Kill old pseudo */
2989 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
2990 retrydahdi = (strcasecmp(c->tech->type, "DAHDI") || (c->audiohooks || c->monitor) ? 1 : 0);
2991 user->dahdichannel = !retrydahdi;
2994 if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2995 f = ast_read_noaudio(c);
3002 if (f->frametype == AST_FRAME_DTMF) {
3003 dtmfstr[0] = f->subclass.integer;
3007 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.codec == AST_FORMAT_SLINEAR)) {
3008 if (user->talk.actual) {
3009 ast_frame_adjust_volume(f, user->talk.actual);
3012 if (ast_test_flag64(confflags, (CONFFLAG_OPTIMIZETALKER | CONFFLAG_MONITORTALKER))) {
3013 if (user->talking == -1) {
3017 res = ast_dsp_silence(dsp, f, &totalsilence);
3018 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
3019 set_user_talking(chan, conf, user, 1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
3022 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
3023 set_user_talking(chan, conf, user, 0, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
3027 /* Absolutely do _not_ use careful_write here...
3028 it is important that we read data from the channel
3029 as fast as it arrives, and feed it into the conference.
3030 The buffering in the pseudo channel will take care of any
3031 timing differences, unless they are so drastic as to lose
3032 audio frames (in which case carefully writing would only
3033 have delayed the audio even further).
3035 /* As it turns out, we do want to use careful write. We just
3036 don't want to block, but we do want to at least *try*
3037 to write out all the samples.
3039 if (user->talking || !ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER)) {
3040 careful_write(fd, f->data.ptr, f->datalen, 0);
3043 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass.integer == '*') && ast_test_flag64(confflags, CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
3044 if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
3045 conf_queue_dtmf(conf, user, f);
3047 if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
3048 ast_log(LOG_WARNING, "Error setting conference\n");
3054 /* if we are entering the menu, and the user has a channel-driver
3055 volume adjustment, clear it
3057 if (!menu_active && user->talk.desired && !user->talk.actual) {
3058 set_talk_volume(user, 0);
3066 dtmf = f->subclass.integer;
3071 case '1': /* *81 Roll call */
3074 if (conf->users == 1) {
3075 if (keepplaying && !ast_streamfile(chan, "conf-onlyperson", chan->language)) {
3076 res = ast_waitstream(chan, AST_DIGIT_ANY);
3077 ast_stopstream(chan);
3081 } else if (conf->users == 2) {
3082 if (keepplaying && !ast_streamfile(chan, "conf-onlyone", chan->language)) {
3083 res = ast_waitstream(chan, AST_DIGIT_ANY);
3084 ast_stopstream(chan);
3089 if (keepplaying && !ast_streamfile(chan, "conf-thereare", chan->language)) {
3090 res = ast_waitstream(chan, AST_DIGIT_ANY);
3091 ast_stopstream(chan);
3096 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
3100 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {