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
32 /*! \li \ref app_meetme.c uses configuration file \ref meetme.conf
33 * \addtogroup configuration_file Configuration Files
37 * \page meetme.conf meetme.conf
38 * \verbinclude meetme.conf.sample
42 <depend>dahdi</depend>
43 <defaultenabled>no</defaultenabled>
44 <support_level>extended</support_level>
45 <replacement>app_confbridge</replacement>
50 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
52 #include <dahdi/user.h>
54 #include "asterisk/lock.h"
55 #include "asterisk/file.h"
56 #include "asterisk/channel.h"
57 #include "asterisk/pbx.h"
58 #include "asterisk/module.h"
59 #include "asterisk/config.h"
60 #include "asterisk/app.h"
61 #include "asterisk/dsp.h"
62 #include "asterisk/musiconhold.h"
63 #include "asterisk/manager.h"
64 #include "asterisk/cli.h"
65 #include "asterisk/say.h"
66 #include "asterisk/utils.h"
67 #include "asterisk/translate.h"
68 #include "asterisk/ulaw.h"
69 #include "asterisk/astobj2.h"
70 #include "asterisk/devicestate.h"
71 #include "asterisk/dial.h"
72 #include "asterisk/causes.h"
73 #include "asterisk/paths.h"
74 #include "asterisk/data.h"
75 #include "asterisk/test.h"
76 #include "asterisk/stasis.h"
77 #include "asterisk/stasis_channels.h"
78 #include "asterisk/stasis_message_router.h"
79 #include "asterisk/json.h"
85 <application name="MeetMe" language="en_US">
87 MeetMe conference bridge.
90 <parameter name="confno">
91 <para>The conference number</para>
93 <parameter name="options">
96 <para>Set admin mode.</para>
99 <para>Set marked mode.</para>
102 <para>Run AGI script specified in <variable>MEETME_AGI_BACKGROUND</variable>
103 Default: <literal>conf-background.agi</literal>.</para>
104 <note><para>This does not work with non-DAHDI channels in the same
105 conference).</para></note>
108 <para>Announce user(s) count on joining a conference.</para>
111 <para>Continue in dialplan when kicked out of conference.</para>
114 <para>Dynamically add conference.</para>
117 <para>Dynamically add conference, prompting for a PIN.</para>
120 <para>Select an empty conference.</para>
123 <para>Select an empty pinless conference.</para>
126 <para>Pass DTMF through the conference.</para>
129 <argument name="x" required="true">
130 <para>The file to playback</para>
132 <para>Play an intro announcement in conference.</para>
135 <para>Announce user join/leave with review.</para>
138 <para>Announce user join/leave without review.</para>
141 <para>Close the conference if there's only one active participant left at exit.</para>
144 <para>Set listen only mode (Listen only, no talking).</para>
147 <para>Set initially muted.</para>
149 <option name="M" hasparams="optional">
150 <para>Enable music on hold when the conference has a single caller. Optionally,
151 specify a musiconhold class to use. If one is not provided, it will use the
152 channel's currently set music class, or <literal>default</literal>.</para>
153 <argument name="class" required="true" />
156 <para>Disable the denoiser. By default, if <literal>func_speex</literal> is loaded, Asterisk
157 will apply a denoiser to channels in the MeetMe conference. However, channel
158 drivers that present audio with a varying rate will experience degraded
159 performance with a denoiser attached. This parameter allows a channel joining
160 the conference to choose not to have a denoiser attached without having to
161 unload <literal>func_speex</literal>.</para>
164 <para>Set talker optimization - treats talkers who aren't speaking as
165 being muted, meaning (a) No encode is done on transmission and (b)
166 Received audio that is not registered as talking is omitted causing no
167 buildup in background noise.</para>
169 <option name="p" hasparams="optional">
170 <para>Allow user to exit the conference by pressing <literal>#</literal> (default)
171 or any of the defined keys. Dial plan execution will continue at the next
172 priority following MeetMe. The key used is set to channel variable
173 <variable>MEETME_EXIT_KEY</variable>.</para>
174 <argument name="keys" required="true" />
176 <para>Option <literal>s</literal> has priority for <literal>*</literal>
177 since it cannot change its activation code.</para>
181 <para>Always prompt for the pin even if it is specified.</para>
184 <para>Quiet mode (don't play enter/leave sounds).</para>
187 <para>Record conference (records as <variable>MEETME_RECORDINGFILE</variable>
188 using format <variable>MEETME_RECORDINGFORMAT</variable>. Default filename is
189 <literal>meetme-conf-rec-${CONFNO}-${UNIQUEID}</literal> and the default format is
193 <para>Present menu (user or admin) when <literal>*</literal> is received
194 (send to menu).</para>
197 <para>Set talk only mode. (Talk only, no listening).</para>
200 <para>Set talker detection (sent to manager interface and meetme list).</para>
202 <option name="v" hasparams="optional">
203 <para>Announce when a user is joining or leaving the conference. Use the voicemail greeting as the announcement.
204 If the i or I options are set, the application will fall back to them if no voicemail greeting can be found.</para>
205 <argument name="mailbox@[context]" required="true">
206 <para>The mailbox and voicemail context to play from. If no context provided, assumed context is default.</para>
209 <option name="w" hasparams="optional">
210 <para>Wait until the marked user enters the conference.</para>
211 <argument name="secs" required="true" />
214 <para>Leave the conference when the last marked user leaves.</para>
217 <para>Allow user to exit the conference by entering a valid single digit
218 extension <variable>MEETME_EXIT_CONTEXT</variable> or the current context
219 if that variable is not defined.</para>
221 <para>Option <literal>s</literal> has priority for <literal>*</literal>
222 since it cannot change its activation code.</para>
226 <para>Do not play message when first person enters</para>
229 <para>Kick the user <replaceable>x</replaceable> seconds <emphasis>after</emphasis> he entered into
230 the conference.</para>
231 <argument name="x" required="true" />
233 <option name="L" argsep=":">
234 <para>Limit the conference to <replaceable>x</replaceable> ms. Play a warning when
235 <replaceable>y</replaceable> ms are left. Repeat the warning every <replaceable>z</replaceable> ms.
236 The following special variables can be used with this option:</para>
238 <variable name="CONF_LIMIT_TIMEOUT_FILE">
239 <para>File to play when time is up.</para>
241 <variable name="CONF_LIMIT_WARNING_FILE">
242 <para>File to play as warning if <replaceable>y</replaceable> is defined. The
243 default is to say the time remaining.</para>
246 <argument name="x" />
247 <argument name="y" />
248 <argument name="z" />
252 <parameter name="pin" />
255 <para>Enters the user into a specified MeetMe conference. If the <replaceable>confno</replaceable>
256 is omitted, the user will be prompted to enter one. User can exit the conference by hangup, or
257 if the <literal>p</literal> option is specified, by pressing <literal>#</literal>.</para>
258 <note><para>The DAHDI kernel modules and a functional DAHDI timing source (see dahdi_test)
259 must be present for conferencing to operate properly. In addition, the chan_dahdi channel driver
260 must be loaded for the <literal>i</literal> and <literal>r</literal> options to operate at
264 <ref type="application">MeetMeCount</ref>
265 <ref type="application">MeetMeAdmin</ref>
266 <ref type="application">MeetMeChannelAdmin</ref>
269 <application name="MeetMeCount" language="en_US">
271 MeetMe participant count.
274 <parameter name="confno" required="true">
275 <para>Conference number.</para>
277 <parameter name="var" />
280 <para>Plays back the number of users in the specified MeetMe conference.
281 If <replaceable>var</replaceable> is specified, playback will be skipped and the value
282 will be returned in the variable. Upon application completion, MeetMeCount will hangup
283 the channel, unless priority <literal>n+1</literal> exists, in which case priority progress will
287 <ref type="application">MeetMe</ref>
290 <application name="MeetMeAdmin" language="en_US">
292 MeetMe conference administration.
295 <parameter name="confno" required="true" />
296 <parameter name="command" required="true">
299 <para>Eject last user that joined.</para>
302 <para>Extend conference end time, if scheduled.</para>
305 <para>Kick one user out of conference.</para>
308 <para>Kick all users out of conference.</para>
311 <para>Unlock conference.</para>
314 <para>Lock conference.</para>
317 <para>Unmute one user.</para>
320 <para>Mute one user.</para>
323 <para>Unmute all users in the conference.</para>
326 <para>Mute all non-admin users in the conference.</para>
329 <para>Reset one user's volume settings.</para>
332 <para>Reset all users volume settings.</para>
335 <para>Lower entire conference speaking volume.</para>
338 <para>Raise entire conference speaking volume.</para>
341 <para>Lower one user's talk volume.</para>
344 <para>Raise one user's talk volume.</para>
347 <para>Lower one user's listen volume.</para>
350 <para>Raise one user's listen volume.</para>
353 <para>Lower entire conference listening volume.</para>
356 <para>Raise entire conference listening volume.</para>
360 <parameter name="user" />
363 <para>Run admin <replaceable>command</replaceable> for conference <replaceable>confno</replaceable>.</para>
364 <para>Will additionally set the variable <variable>MEETMEADMINSTATUS</variable> with one of
365 the following values:</para>
367 <variable name="MEETMEADMINSTATUS">
368 <value name="NOPARSE">
371 <value name="NOTFOUND">
372 User specified was not found.
374 <value name="FAILED">
375 Another failure occurred.
378 The operation was completed successfully.
384 <ref type="application">MeetMe</ref>
387 <application name="MeetMeChannelAdmin" language="en_US">
389 MeetMe conference Administration (channel specific).
392 <parameter name="channel" required="true" />
393 <parameter name="command" required="true">
396 <para>Kick the specified user out of the conference he is in.</para>
399 <para>Unmute the specified user.</para>
402 <para>Mute the specified user.</para>
408 <para>Run admin <replaceable>command</replaceable> for a specific
409 <replaceable>channel</replaceable> in any conference.</para>
412 <application name="SLAStation" language="en_US">
414 Shared Line Appearance Station.
417 <parameter name="station" required="true">
418 <para>Station name</para>
422 <para>This application should be executed by an SLA station. The argument depends
423 on how the call was initiated. If the phone was just taken off hook, then the argument
424 <replaceable>station</replaceable> should be just the station name. If the call was
425 initiated by pressing a line key, then the station name should be preceded by an underscore
426 and the trunk name associated with that line button.</para>
427 <para>For example: <literal>station1_line1</literal></para>
428 <para>On exit, this application will set the variable <variable>SLASTATION_STATUS</variable> to
429 one of the following values:</para>
431 <variable name="SLASTATION_STATUS">
432 <value name="FAILURE" />
433 <value name="CONGESTION" />
434 <value name="SUCCESS" />
439 <application name="SLATrunk" language="en_US">
441 Shared Line Appearance Trunk.
444 <parameter name="trunk" required="true">
445 <para>Trunk name</para>
447 <parameter name="options">
449 <option name="M" hasparams="optional">
450 <para>Play back the specified MOH <replaceable>class</replaceable>
451 instead of ringing</para>
452 <argument name="class" required="true" />
458 <para>This application should be executed by an SLA trunk on an inbound call. The channel calling
459 this application should correspond to the SLA trunk with the name <replaceable>trunk</replaceable>
460 that is being passed as an argument.</para>
461 <para>On exit, this application will set the variable <variable>SLATRUNK_STATUS</variable> to
462 one of the following values:</para>
464 <variable name="SLATRUNK_STATUS">
465 <value name="FAILURE" />
466 <value name="SUCCESS" />
467 <value name="UNANSWERED" />
468 <value name="RINGTIMEOUT" />
473 <function name="MEETME_INFO" language="en_US">
475 Query a given conference of various properties.
478 <parameter name="keyword" required="true">
479 <para>Options:</para>
482 <para>Boolean of whether the corresponding conference is locked.</para>
484 <enum name="parties">
485 <para>Number of parties in a given conference</para>
487 <enum name="activity">
488 <para>Duration of conference in seconds.</para>
490 <enum name="dynamic">
491 <para>Boolean of whether the corresponding conference is dynamic.</para>
495 <parameter name="confno" required="true">
496 <para>Conference number to retrieve information from.</para>
501 <ref type="application">MeetMe</ref>
502 <ref type="application">MeetMeCount</ref>
503 <ref type="application">MeetMeAdmin</ref>
504 <ref type="application">MeetMeChannelAdmin</ref>
507 <manager name="MeetmeMute" language="en_US">
512 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
513 <parameter name="Meetme" required="true" />
514 <parameter name="Usernum" required="true" />
519 <manager name="MeetmeUnmute" language="en_US">
521 Unmute a Meetme user.
524 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
525 <parameter name="Meetme" required="true" />
526 <parameter name="Usernum" required="true" />
531 <manager name="MeetmeList" language="en_US">
533 List participants in a conference.
536 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
537 <parameter name="Conference" required="false">
538 <para>Conference number.</para>
542 <para>Lists all users in a particular MeetMe conference.
543 MeetmeList will follow as separate events, followed by a final event called
544 MeetmeListComplete.</para>
547 <manager name="MeetmeListRooms" language="en_US">
549 List active conferences.
552 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
555 <para>Lists data about all active conferences.
556 MeetmeListRooms will follow as separate events, followed by a final event called
557 MeetmeListRoomsComplete.</para>
560 <managerEvent language="en_US" name="MeetmeJoin">
561 <managerEventInstance class="EVENT_FLAG_CALL">
562 <synopsis>Raised when a user joins a MeetMe conference.</synopsis>
564 <parameter name="Meetme">
565 <para>The identifier for the MeetMe conference.</para>
567 <parameter name="Usernum">
568 <para>The identifier of the MeetMe user who joined.</para>
573 <ref type="managerEvent">MeetmeLeave</ref>
574 <ref type="application">MeetMe</ref>
576 </managerEventInstance>
578 <managerEvent language="en_US" name="MeetmeLeave">
579 <managerEventInstance class="EVENT_FLAG_CALL">
580 <synopsis>Raised when a user leaves a MeetMe conference.</synopsis>
582 <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter)" />
584 <parameter name="Duration">
585 <para>The length of time in seconds that the Meetme user was in the conference.</para>
589 <ref type="managerEvent">MeetmeJoin</ref>
591 </managerEventInstance>
593 <managerEvent language="en_US" name="MeetmeEnd">
594 <managerEventInstance class="EVENT_FLAG_CALL">
595 <synopsis>Raised when a MeetMe conference ends.</synopsis>
597 <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
600 <ref type="managerEvent">MeetmeJoin</ref>
602 </managerEventInstance>
604 <managerEvent language="en_US" name="MeetmeTalkRequest">
605 <managerEventInstance class="EVENT_FLAG_CALL">
606 <synopsis>Raised when a MeetMe user has started talking.</synopsis>
608 <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter)" />
610 <parameter name="Duration">
611 <para>The length of time in seconds that the Meetme user has been in the conference at the time of this event.</para>
613 <parameter name="Status">
620 </managerEventInstance>
622 <managerEvent language="en_US" name="MeetmeTalking">
623 <managerEventInstance class="EVENT_FLAG_CALL">
624 <synopsis>Raised when a MeetMe user begins or ends talking.</synopsis>
626 <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter)" />
628 <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeTalkRequest']/managerEventInstance/syntax/parameter)" />
630 </managerEventInstance>
632 <managerEvent language="en_US" name="MeetmeMute">
633 <managerEventInstance class="EVENT_FLAG_CALL">
634 <synopsis>Raised when a MeetMe user is muted or unmuted.</synopsis>
636 <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter)" />
638 <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeTalkRequest']/managerEventInstance/syntax/parameter)" />
640 </managerEventInstance>
644 #define CONFIG_FILE_NAME "meetme.conf"
645 #define SLA_CONFIG_FILE "sla.conf"
646 #define STR_CONCISE "concise"
648 /*! each buffer is 20ms, so this is 640ms total */
649 #define DEFAULT_AUDIO_BUFFERS 32
651 /*! String format for scheduled conferences */
652 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
655 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
656 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
657 ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */
658 /*! User has requested to speak */
659 ADMINFLAG_T_REQUEST = (1 << 4),
660 ADMINFLAG_HANGUP = (1 << 5), /*!< User will be leaving the conference */
663 #define MEETME_DELAYDETECTTALK 300
664 #define MEETME_DELAYDETECTENDTALK 1000
666 #define AST_FRAME_BITS 32
673 enum entrance_sound {
678 enum recording_state {
680 MEETME_RECORD_STARTED,
681 MEETME_RECORD_ACTIVE,
682 MEETME_RECORD_TERMINATE
685 #define CONF_SIZE 320
688 /*! user has admin access on the conference */
689 CONFFLAG_ADMIN = (1 << 0),
690 /*! If set the user can only receive audio from the conference */
691 CONFFLAG_MONITOR = (1 << 1),
692 /*! If set asterisk will exit conference when key defined in p() option is pressed */
693 CONFFLAG_KEYEXIT = (1 << 2),
694 /*! If set asterisk will provide a menu to the user when '*' is pressed */
695 CONFFLAG_STARMENU = (1 << 3),
696 /*! If set the use can only send audio to the conference */
697 CONFFLAG_TALKER = (1 << 4),
698 /*! If set there will be no enter or leave sounds */
699 CONFFLAG_QUIET = (1 << 5),
700 /*! If set, when user joins the conference, they will be told the number
701 * of users that are already in */
702 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
703 /*! Set to run AGI Script in Background */
704 CONFFLAG_AGI = (1 << 7),
705 /*! Set to have music on hold when user is alone in conference */
706 CONFFLAG_MOH = (1 << 8),
707 /*! If set, the channel will leave the conference if all marked users leave */
708 CONFFLAG_MARKEDEXIT = (1 << 9),
709 /*! If set, the MeetMe will wait until a marked user enters */
710 CONFFLAG_WAITMARKED = (1 << 10),
711 /*! If set, the MeetMe will exit to the specified context */
712 CONFFLAG_EXIT_CONTEXT = (1 << 11),
713 /*! If set, the user will be marked */
714 CONFFLAG_MARKEDUSER = (1 << 12),
715 /*! If set, user will be ask record name on entry of conference */
716 CONFFLAG_INTROUSER = (1 << 13),
717 /*! If set, the MeetMe will be recorded */
718 CONFFLAG_RECORDCONF = (1<< 14),
719 /*! If set, the user will be monitored if the user is talking or not */
720 CONFFLAG_MONITORTALKER = (1 << 15),
721 CONFFLAG_DYNAMIC = (1 << 16),
722 CONFFLAG_DYNAMICPIN = (1 << 17),
723 CONFFLAG_EMPTY = (1 << 18),
724 CONFFLAG_EMPTYNOPIN = (1 << 19),
725 CONFFLAG_ALWAYSPROMPT = (1 << 20),
726 /*! If set, treat talking users as muted users */
727 CONFFLAG_OPTIMIZETALKER = (1 << 21),
728 /*! If set, won't speak the extra prompt when the first person
729 * enters the conference */
730 CONFFLAG_NOONLYPERSON = (1 << 22),
731 /*! If set, user will be asked to record name on entry of conference
733 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
734 /*! If set, the user will be initially self-muted */
735 CONFFLAG_STARTMUTED = (1 << 24),
736 /*! Pass DTMF through the conference */
737 CONFFLAG_PASS_DTMF = (1 << 25),
738 CONFFLAG_SLA_STATION = (1 << 26),
739 CONFFLAG_SLA_TRUNK = (1 << 27),
740 /*! If set, the user should continue in the dialplan if kicked out */
741 CONFFLAG_KICK_CONTINUE = (1 << 28),
742 CONFFLAG_DURATION_STOP = (1 << 29),
743 CONFFLAG_DURATION_LIMIT = (1 << 30),
746 /* These flags are defined separately because we ran out of bits that an enum can be used to represent.
747 If you add new flags, be sure to do it in the same way that these are. */
748 /*! Do not write any audio to this channel until the state is up. */
749 #define CONFFLAG_NO_AUDIO_UNTIL_UP (1ULL << 31)
750 #define CONFFLAG_INTROMSG (1ULL << 32) /*!< If set play an intro announcement at start of conference */
751 #define CONFFLAG_INTROUSER_VMREC (1ULL << 33)
752 /*! If there's only one person left in a conference when someone leaves, kill the conference */
753 #define CONFFLAG_KILL_LAST_MAN_STANDING (1ULL << 34)
754 /*! If set, don't enable a denoiser for the channel */
755 #define CONFFLAG_DONT_DENOISE (1ULL << 35)
758 OPT_ARG_WAITMARKED = 0,
759 OPT_ARG_EXITKEYS = 1,
760 OPT_ARG_DURATION_STOP = 2,
761 OPT_ARG_DURATION_LIMIT = 3,
762 OPT_ARG_MOH_CLASS = 4,
763 OPT_ARG_INTROMSG = 5,
764 OPT_ARG_INTROUSER_VMREC = 6,
765 OPT_ARG_ARRAY_SIZE = 7,
768 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
769 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
770 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
771 AST_APP_OPTION('b', CONFFLAG_AGI ),
772 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
773 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
774 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
775 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
776 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
777 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
778 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
779 AST_APP_OPTION_ARG('G', CONFFLAG_INTROMSG, OPT_ARG_INTROMSG ),
780 AST_APP_OPTION_ARG('v', CONFFLAG_INTROUSER_VMREC , OPT_ARG_INTROUSER_VMREC),
781 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
782 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
783 AST_APP_OPTION('k', CONFFLAG_KILL_LAST_MAN_STANDING ),
784 AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
785 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
786 AST_APP_OPTION('n', CONFFLAG_DONT_DENOISE ),
787 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
788 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
789 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
790 AST_APP_OPTION('q', CONFFLAG_QUIET ),
791 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
792 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
793 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
794 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
795 AST_APP_OPTION('t', CONFFLAG_TALKER ),
796 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
797 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
798 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
799 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
800 AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
801 AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
804 static const char * const app = "MeetMe";
805 static const char * const app2 = "MeetMeCount";
806 static const char * const app3 = "MeetMeAdmin";
807 static const char * const app4 = "MeetMeChannelAdmin";
808 static const char * const slastation_app = "SLAStation";
809 static const char * const slatrunk_app = "SLATrunk";
811 /* Lookup RealTime conferences based on confno and current time */
812 static int rt_schedule;
813 static int fuzzystart;
814 static int earlyalert;
818 /*! Log participant count to the RealTime backend */
819 static int rt_log_members;
821 #define MAX_CONFNUM 80
823 #define OPTIONS_LEN 100
825 /* Enough space for "<conference #>,<pin>,<admin pin>" followed by a 0 byte. */
826 #define MAX_SETTINGS (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)
833 struct announce_listitem {
834 AST_LIST_ENTRY(announce_listitem) entry;
835 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
836 char language[MAX_LANGUAGE];
837 struct ast_channel *confchan;
840 enum announcetypes announcetype;
843 /*! \brief The MeetMe Conference object */
844 struct ast_conference {
845 ast_mutex_t playlock; /*!< Conference specific lock (players) */
846 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
847 char confno[MAX_CONFNUM]; /*!< Conference */
848 struct ast_channel *chan; /*!< Announcements channel */
849 struct ast_channel *lchan; /*!< Listen/Record channel */
850 int fd; /*!< Announcements fd */
851 int dahdiconf; /*!< DAHDI Conf # */
852 int users; /*!< Number of active users */
853 int markedusers; /*!< Number of marked users */
854 int maxusers; /*!< Participant limit if scheduled */
855 int endalert; /*!< When to play conf ending message */
856 time_t start; /*!< Start time (s) */
857 int refcount; /*!< reference count of usage */
858 enum recording_state recording:2; /*!< recording status */
859 unsigned int isdynamic:1; /*!< Created on the fly? */
860 unsigned int locked:1; /*!< Is the conference locked? */
861 unsigned int gmuted:1; /*!< Is the conference globally muted? (all non-admins) */
862 pthread_t recordthread; /*!< thread for recording */
863 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
864 pthread_attr_t attr; /*!< thread attribute */
865 char *recordingfilename; /*!< Filename to record the Conference into */
866 char *recordingformat; /*!< Format to record the Conference in */
867 char pin[MAX_PIN]; /*!< If protected by a PIN */
868 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
870 long endtime; /*!< When to end the conf if scheduled */
871 const char *useropts; /*!< RealTime user flags */
872 const char *adminopts; /*!< RealTime moderator flags */
873 const char *bookid; /*!< RealTime conference id */
874 struct ast_frame *transframe[32];
875 struct ast_frame *origframe;
876 struct ast_trans_pvt *transpath[32];
877 struct ao2_container *usercontainer;
878 AST_LIST_ENTRY(ast_conference) list;
879 /* announce_thread related data */
880 pthread_t announcethread;
881 ast_mutex_t announcethreadlock;
882 unsigned int announcethread_stop:1;
883 ast_cond_t announcelist_addition;
884 AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
885 ast_mutex_t announcelistlock;
888 static AST_LIST_HEAD_STATIC(confs, ast_conference);
890 static unsigned int conf_map[1024] = {0, };
893 int desired; /*!< Desired volume adjustment */
894 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
897 /*! \brief The MeetMe User object */
898 struct ast_conf_user {
899 int user_no; /*!< User Number */
900 struct ast_flags64 userflags; /*!< Flags as set in the conference */
901 int adminflags; /*!< Flags set by the Admin */
902 struct ast_channel *chan; /*!< Connected channel */
903 int talking; /*!< Is user talking */
904 int dahdichannel; /*!< Is a DAHDI channel */
905 char usrvalue[50]; /*!< Custom User Value */
906 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
907 time_t jointime; /*!< Time the user joined the conference */
908 time_t kicktime; /*!< Time the user will be kicked from the conference */
909 struct timeval start_time; /*!< Time the user entered into the conference */
910 long timelimit; /*!< Time limit for the user to be in the conference L(x:y:z) */
911 long play_warning; /*!< Play a warning when 'y' ms are left */
912 long warning_freq; /*!< Repeat the warning every 'z' ms */
913 const char *warning_sound; /*!< File to play as warning if 'y' is defined */
914 const char *end_sound; /*!< File to play when time is up. */
916 struct volume listen;
917 AST_LIST_ENTRY(ast_conf_user) list;
920 enum sla_which_trunk_refs {
925 enum sla_trunk_state {
926 SLA_TRUNK_STATE_IDLE,
927 SLA_TRUNK_STATE_RINGING,
929 SLA_TRUNK_STATE_ONHOLD,
930 SLA_TRUNK_STATE_ONHOLD_BYME,
933 enum sla_hold_access {
934 /*! This means that any station can put it on hold, and any station
935 * can retrieve the call from hold. */
937 /*! This means that only the station that put the call on hold may
938 * retrieve it from hold. */
942 struct sla_trunk_ref;
945 AST_RWLIST_ENTRY(sla_station) entry;
946 AST_DECLARE_STRING_FIELDS(
947 AST_STRING_FIELD(name);
948 AST_STRING_FIELD(device);
949 AST_STRING_FIELD(autocontext);
951 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
952 struct ast_dial *dial;
953 /*! Ring timeout for this station, for any trunk. If a ring timeout
954 * is set for a specific trunk on this station, that will take
955 * priority over this value. */
956 unsigned int ring_timeout;
957 /*! Ring delay for this station, for any trunk. If a ring delay
958 * is set for a specific trunk on this station, that will take
959 * priority over this value. */
960 unsigned int ring_delay;
961 /*! This option uses the values in the sla_hold_access enum and sets the
962 * access control type for hold on this station. */
963 unsigned int hold_access:1;
964 /*! Mark used during reload processing */
969 * \brief A reference to a station
971 * This struct looks near useless at first glance. However, its existence
972 * in the list of stations in sla_trunk means that this station references
973 * that trunk. We use the mark to keep track of whether it needs to be
974 * removed from the sla_trunk's list of stations during a reload.
976 struct sla_station_ref {
977 AST_LIST_ENTRY(sla_station_ref) entry;
978 struct sla_station *station;
979 /*! Mark used during reload processing */
984 AST_DECLARE_STRING_FIELDS(
985 AST_STRING_FIELD(name);
986 AST_STRING_FIELD(device);
987 AST_STRING_FIELD(autocontext);
989 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
990 /*! Number of stations that use this trunk */
991 unsigned int num_stations;
992 /*! Number of stations currently on a call with this trunk */
993 unsigned int active_stations;
994 /*! Number of stations that have this trunk on hold. */
995 unsigned int hold_stations;
996 struct ast_channel *chan;
997 unsigned int ring_timeout;
998 /*! If set to 1, no station will be able to join an active call with
1000 unsigned int barge_disabled:1;
1001 /*! This option uses the values in the sla_hold_access enum and sets the
1002 * access control type for hold on this trunk. */
1003 unsigned int hold_access:1;
1004 /*! Whether this trunk is currently on hold, meaning that once a station
1005 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
1006 unsigned int on_hold:1;
1007 /*! Mark used during reload processing */
1008 unsigned int mark:1;
1012 * \brief A station's reference to a trunk
1014 * An sla_station keeps a list of trunk_refs. This holds metadata about the
1015 * stations usage of the trunk.
1017 struct sla_trunk_ref {
1018 AST_LIST_ENTRY(sla_trunk_ref) entry;
1019 struct sla_trunk *trunk;
1020 enum sla_trunk_state state;
1021 struct ast_channel *chan;
1022 /*! Ring timeout to use when this trunk is ringing on this specific
1023 * station. This takes higher priority than a ring timeout set at
1024 * the station level. */
1025 unsigned int ring_timeout;
1026 /*! Ring delay to use when this trunk is ringing on this specific
1027 * station. This takes higher priority than a ring delay set at
1028 * the station level. */
1029 unsigned int ring_delay;
1030 /*! Mark used during reload processing */
1031 unsigned int mark:1;
1034 static struct ao2_container *sla_stations;
1035 static struct ao2_container *sla_trunks;
1037 static const char sla_registrar[] = "SLA";
1039 /*! \brief Event types that can be queued up for the SLA thread */
1040 enum sla_event_type {
1041 /*! A station has put the call on hold */
1043 /*! The state of a dial has changed */
1044 SLA_EVENT_DIAL_STATE,
1045 /*! The state of a ringing trunk has changed */
1046 SLA_EVENT_RINGING_TRUNK,
1050 enum sla_event_type type;
1051 struct sla_station *station;
1052 struct sla_trunk_ref *trunk_ref;
1053 AST_LIST_ENTRY(sla_event) entry;
1056 /*! \brief A station that failed to be dialed
1057 * \note Only used by the SLA thread. */
1058 struct sla_failed_station {
1059 struct sla_station *station;
1060 struct timeval last_try;
1061 AST_LIST_ENTRY(sla_failed_station) entry;
1064 /*! \brief A trunk that is ringing */
1065 struct sla_ringing_trunk {
1066 struct sla_trunk *trunk;
1067 /*! The time that this trunk started ringing */
1068 struct timeval ring_begin;
1069 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
1070 AST_LIST_ENTRY(sla_ringing_trunk) entry;
1073 enum sla_station_hangup {
1074 SLA_STATION_HANGUP_NORMAL,
1075 SLA_STATION_HANGUP_TIMEOUT,
1078 /*! \brief A station that is ringing */
1079 struct sla_ringing_station {
1080 struct sla_station *station;
1081 /*! The time that this station started ringing */
1082 struct timeval ring_begin;
1083 AST_LIST_ENTRY(sla_ringing_station) entry;
1087 * \brief A structure for data used by the sla thread
1090 /*! The SLA thread ID */
1094 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
1095 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
1096 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
1097 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
1098 unsigned int stop:1;
1099 /*! Attempt to handle CallerID, even though it is known not to work
1100 * properly in some situations. */
1101 unsigned int attempt_callerid:1;
1103 .thread = AST_PTHREADT_NULL,
1106 /*! \brief The number of audio buffers to be allocated on pseudo channels
1107 * when in a conference */
1108 static int audio_buffers;
1110 /*! \brief Map 'volume' levels from -5 through +5 into decibel (dB)
1111 * settings for channel drivers.
1113 * \note these are not a straight linear-to-dB
1114 * conversion... the numbers have been modified
1115 * to give the user a better level of adjustability.
1117 static const char gain_map[] = {
1131 /* Routes the various meetme message types to the meetme stasis callback function to turn them into events */
1132 static struct stasis_message_router *meetme_event_message_router;
1134 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_join_type);
1135 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_leave_type);
1136 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_end_type);
1137 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_mute_type);
1138 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_talking_type);
1139 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_talk_request_type);
1141 static void meetme_stasis_cb(void *data, struct stasis_subscription *sub,
1142 struct stasis_topic *topic, struct stasis_message *message);
1144 static void meetme_stasis_cleanup(void)
1146 if (meetme_event_message_router) {
1147 stasis_message_router_unsubscribe(meetme_event_message_router);
1148 meetme_event_message_router = NULL;
1151 STASIS_MESSAGE_TYPE_CLEANUP(meetme_join_type);
1152 STASIS_MESSAGE_TYPE_CLEANUP(meetme_leave_type);
1153 STASIS_MESSAGE_TYPE_CLEANUP(meetme_end_type);
1154 STASIS_MESSAGE_TYPE_CLEANUP(meetme_mute_type);
1155 STASIS_MESSAGE_TYPE_CLEANUP(meetme_talking_type);
1156 STASIS_MESSAGE_TYPE_CLEANUP(meetme_talk_request_type);
1159 static int meetme_stasis_init(void)
1162 STASIS_MESSAGE_TYPE_INIT(meetme_join_type);
1163 STASIS_MESSAGE_TYPE_INIT(meetme_leave_type);
1164 STASIS_MESSAGE_TYPE_INIT(meetme_end_type);
1165 STASIS_MESSAGE_TYPE_INIT(meetme_mute_type);
1166 STASIS_MESSAGE_TYPE_INIT(meetme_talking_type);
1167 STASIS_MESSAGE_TYPE_INIT(meetme_talk_request_type);
1169 meetme_event_message_router = stasis_message_router_create(
1170 ast_channel_topic_all_cached());
1172 if (!meetme_event_message_router) {
1173 meetme_stasis_cleanup();
1177 if (stasis_message_router_add(meetme_event_message_router,
1181 meetme_stasis_cleanup();
1185 if (stasis_message_router_add(meetme_event_message_router,
1186 meetme_leave_type(),
1189 meetme_stasis_cleanup();
1193 if (stasis_message_router_add(meetme_event_message_router,
1197 meetme_stasis_cleanup();
1201 if (stasis_message_router_add(meetme_event_message_router,
1205 meetme_stasis_cleanup();
1209 if (stasis_message_router_add(meetme_event_message_router,
1210 meetme_talking_type(),
1213 meetme_stasis_cleanup();
1217 if (stasis_message_router_add(meetme_event_message_router,
1218 meetme_talk_request_type(),
1221 meetme_stasis_cleanup();
1228 static void meetme_stasis_cb(void *data, struct stasis_subscription *sub,
1229 struct stasis_topic *topic, struct stasis_message *message)
1231 struct ast_channel_blob *channel_blob = stasis_message_data(message);
1232 struct stasis_message_type *message_type;
1234 const char *conference_num;
1236 struct ast_json *json_cur;
1237 RAII_VAR(struct ast_str *, channel_text, NULL, ast_free);
1238 RAII_VAR(struct ast_str *, extra_text, NULL, ast_free);
1240 if (!channel_blob) {
1245 message_type = stasis_message_type(message);
1247 if (!message_type) {
1252 if (message_type == meetme_join_type()) {
1253 event = "MeetmeJoin";
1254 } else if (message_type == meetme_leave_type()) {
1255 event = "MeetmeLeave";
1256 } else if (message_type == meetme_end_type()) {
1257 event = "MeetmeEnd";
1258 } else if (message_type == meetme_mute_type()) {
1259 event = "MeetmeMute";
1260 } else if (message_type == meetme_talking_type()) {
1261 event = "MeetmeTalking";
1262 } else if (message_type == meetme_talk_request_type()) {
1263 event = "MeetmeTalkRequest";
1274 conference_num = ast_json_string_get(ast_json_object_get(channel_blob->blob, "Meetme"));
1275 if (!conference_num) {
1280 status = ast_json_string_get(ast_json_object_get(channel_blob->blob, "status"));
1282 ast_str_append_event_header(&extra_text, "Status", status);
1285 if (channel_blob->snapshot) {
1286 channel_text = ast_manager_build_channel_state_string(channel_blob->snapshot);
1289 if ((json_cur = ast_json_object_get(channel_blob->blob, "user"))) {
1290 int user_number = ast_json_integer_get(json_cur);
1291 RAII_VAR(struct ast_str *, user_prop_str, ast_str_create(32), ast_free);
1292 if (!user_prop_str) {
1296 ast_str_set(&user_prop_str, 0, "%d", user_number);
1297 ast_str_append_event_header(&extra_text, "User", ast_str_buffer(user_prop_str));
1299 if ((json_cur = ast_json_object_get(channel_blob->blob, "duration"))) {
1300 int duration = ast_json_integer_get(json_cur);
1301 ast_str_set(&user_prop_str, 0, "%d", duration);
1302 ast_str_append_event_header(&extra_text, "Duration", ast_str_buffer(user_prop_str));
1308 manager_event(EVENT_FLAG_CALL, event,
1313 channel_text ? ast_str_buffer(channel_text) : "",
1314 extra_text ? ast_str_buffer(extra_text) : "");
1319 * \brief Build a json object from a status value for inclusion in json extras for meetme_stasis_generate_msg
1322 * \param on if true, then status is on. Otherwise status is off
1323 * \retval NULL on failure to allocate the JSON blob.
1324 * \retval pointer to the JSON blob if successful.
1326 static struct ast_json *status_to_json(int on)
1328 struct ast_json *json_object = ast_json_pack("{s: s}",
1329 "status", on ? "on" : "off");
1336 * \brief Generate a stasis message associated with a meetme event
1339 * \param meetme_confere The conference responsible for generating this message
1340 * \param chan The channel involved in the message (NULL allowed)
1341 * \param user The conference user involved in the message (NULL allowed)
1342 * \param message_type the type the stasis message being generated
1343 * \param extras Additional json fields desired for inclusion
1345 static void meetme_stasis_generate_msg(struct ast_conference *meetme_conference, struct ast_channel *chan,
1346 struct ast_conf_user *user, struct stasis_message_type *message_type, struct ast_json *extras)
1348 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
1349 RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
1351 json_object = ast_json_pack("{s: s}",
1352 "Meetme", meetme_conference->confno);
1359 ast_json_object_update(json_object, extras);
1363 struct timeval now = ast_tvnow();
1364 long duration = (long)(now.tv_sec - user->jointime);
1365 RAII_VAR(struct ast_json *, json_user, ast_json_integer_create(user->user_no), ast_json_unref);
1366 RAII_VAR(struct ast_json *, json_user_duration, NULL, ast_json_unref);
1368 if (ast_json_object_set(json_object, "user", json_user)) {
1374 json_user_duration = ast_json_integer_create(duration);
1376 if (!json_user_duration) {
1380 if (ast_json_object_set(json_object, "duration", json_user_duration)) {
1383 json_user_duration = NULL;
1387 msg = ast_channel_blob_create(chan, message_type, json_object);
1393 stasis_publish(ast_channel_topic(chan), msg);
1396 static int admin_exec(struct ast_channel *chan, const char *data);
1397 static void *recordthread(void *args);
1399 static const char *istalking(int x)
1404 return "(unmonitored)";
1406 return "(not talking)";
1409 static int careful_write(int fd, unsigned char *data, int len, int block)
1416 x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
1417 res = ioctl(fd, DAHDI_IOMUX, &x);
1421 res = write(fd, data, len);
1423 if (errno != EAGAIN) {
1424 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
1436 static int set_talk_volume(struct ast_conf_user *user, int volume)
1440 /* attempt to make the adjustment in the channel driver;
1441 if successful, don't adjust in the frame reading routine
1443 gain_adjust = gain_map[volume + 5];
1445 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1448 static int set_listen_volume(struct ast_conf_user *user, int volume)
1452 /* attempt to make the adjustment in the channel driver;
1453 if successful, don't adjust in the frame reading routine
1455 gain_adjust = gain_map[volume + 5];
1457 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1460 static void tweak_volume(struct volume *vol, enum volume_action action)
1464 switch (vol->desired) {
1479 switch (vol->desired) {
1495 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
1497 tweak_volume(&user->talk, action);
1498 /* attempt to make the adjustment in the channel driver;
1499 if successful, don't adjust in the frame reading routine
1501 if (!set_talk_volume(user, user->talk.desired))
1502 user->talk.actual = 0;
1504 user->talk.actual = user->talk.desired;
1507 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
1509 tweak_volume(&user->listen, action);
1510 /* attempt to make the adjustment in the channel driver;
1511 if successful, don't adjust in the frame reading routine
1513 if (!set_listen_volume(user, user->listen.desired))
1514 user->listen.actual = 0;
1516 user->listen.actual = user->listen.desired;
1519 static void reset_volumes(struct ast_conf_user *user)
1521 signed char zero_volume = 0;
1523 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
1524 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
1527 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
1529 unsigned char *data;
1533 ast_test_suite_event_notify("CONFPLAY", "Channel: %s\r\n"
1534 "Conference: %s\r\n"
1536 ast_channel_name(chan),
1540 if (!ast_check_hangup(chan))
1541 res = ast_autoservice_start(chan);
1543 AST_LIST_LOCK(&confs);
1548 len = sizeof(enter);
1552 len = sizeof(leave);
1559 careful_write(conf->fd, data, len, 1);
1562 AST_LIST_UNLOCK(&confs);
1565 ast_autoservice_stop(chan);
1568 static int user_no_cmp(void *obj, void *arg, int flags)
1570 struct ast_conf_user *user = obj;
1573 if (user->user_no == *user_no) {
1574 return (CMP_MATCH | CMP_STOP);
1580 static int user_max_cmp(void *obj, void *arg, int flags)
1582 struct ast_conf_user *user = obj;
1585 if (user->user_no > *max_no) {
1586 *max_no = user->user_no;
1593 * \brief Find or create a conference
1595 * \param confno The conference name/number
1596 * \param pin The regular user pin
1597 * \param pinadmin The admin pin
1598 * \param make Make the conf if it doesn't exist
1599 * \param dynamic Mark the newly created conference as dynamic
1600 * \param refcount How many references to mark on the conference
1601 * \param chan The asterisk channel
1604 * \return A pointer to the conference struct, or NULL if it wasn't found and
1605 * make or dynamic were not set.
1607 static struct ast_conference *build_conf(const char *confno, const char *pin,
1608 const char *pinadmin, int make, int dynamic, int refcount,
1609 const struct ast_channel *chan, struct ast_test *test)
1611 struct ast_conference *cnf;
1612 struct dahdi_confinfo dahdic = { 0, };
1614 struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
1615 struct ast_format tmp_fmt;
1617 AST_LIST_LOCK(&confs);
1619 AST_LIST_TRAVERSE(&confs, cnf, list) {
1620 if (!strcmp(confno, cnf->confno))
1624 if (cnf || (!make && !dynamic) || !cap_slin)
1627 ast_format_cap_add(cap_slin, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0));
1628 /* Make a new one */
1629 if (!(cnf = ast_calloc(1, sizeof(*cnf))) ||
1630 !(cnf->usercontainer = ao2_container_alloc(1, NULL, user_no_cmp))) {
1634 ast_mutex_init(&cnf->playlock);
1635 ast_mutex_init(&cnf->listenlock);
1636 cnf->recordthread = AST_PTHREADT_NULL;
1637 ast_mutex_init(&cnf->recordthreadlock);
1638 cnf->announcethread = AST_PTHREADT_NULL;
1639 ast_mutex_init(&cnf->announcethreadlock);
1640 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
1641 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
1642 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
1643 ast_copy_string(cnf->uniqueid, ast_channel_uniqueid(chan), sizeof(cnf->uniqueid));
1645 /* Setup a new dahdi conference */
1647 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1648 cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
1649 if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
1651 /* if we are creating a conference for a unit test, it is not neccesary
1652 * to open a pseudo channel, so, if we fail continue creating
1653 * the conference. */
1654 ast_test_status_update(test, "Unable to open DAHDI pseudo device\n");
1656 ast_log(LOG_WARNING, "Unable to open DAHDI pseudo device\n");
1659 ao2_ref(cnf->usercontainer, -1);
1660 ast_mutex_destroy(&cnf->playlock);
1661 ast_mutex_destroy(&cnf->listenlock);
1662 ast_mutex_destroy(&cnf->recordthreadlock);
1663 ast_mutex_destroy(&cnf->announcethreadlock);
1670 cnf->dahdiconf = dahdic.confno;
1672 /* Setup a new channel for playback of audio files */
1673 cnf->chan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL);
1675 ast_set_read_format_by_id(cnf->chan, AST_FORMAT_SLINEAR);
1676 ast_set_write_format_by_id(cnf->chan, AST_FORMAT_SLINEAR);
1678 dahdic.confno = cnf->dahdiconf;
1679 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1680 if (ioctl(ast_channel_fd(cnf->chan, 0), DAHDI_SETCONF, &dahdic)) {
1682 ast_test_status_update(test, "Error setting conference on pseudo channel\n");
1684 ast_log(LOG_WARNING, "Error setting conference\n");
1686 ast_hangup(cnf->chan);
1689 ao2_ref(cnf->usercontainer, -1);
1690 ast_mutex_destroy(&cnf->playlock);
1691 ast_mutex_destroy(&cnf->listenlock);
1692 ast_mutex_destroy(&cnf->recordthreadlock);
1693 ast_mutex_destroy(&cnf->announcethreadlock);
1700 /* Fill the conference struct */
1701 cnf->start = time(NULL);
1702 cnf->maxusers = 0x7fffffff;
1703 cnf->isdynamic = dynamic ? 1 : 0;
1704 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
1705 AST_LIST_INSERT_HEAD(&confs, cnf, list);
1707 /* Reserve conference number in map */
1708 if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1709 conf_map[confno_int] = 1;
1712 cap_slin = ast_format_cap_destroy(cap_slin);
1714 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
1716 AST_LIST_UNLOCK(&confs);
1721 static char *complete_confno(const char *word, int state)
1723 struct ast_conference *cnf;
1726 int len = strlen(word);
1728 AST_LIST_LOCK(&confs);
1729 AST_LIST_TRAVERSE(&confs, cnf, list) {
1730 if (!strncmp(word, cnf->confno, len) && ++which > state) {
1731 /* dup before releasing the lock */
1732 ret = ast_strdup(cnf->confno);
1736 AST_LIST_UNLOCK(&confs);
1740 static char *complete_userno(struct ast_conference *cnf, const char *word, int state)
1743 struct ao2_iterator iter;
1744 struct ast_conf_user *usr;
1747 int len = strlen(word);
1749 iter = ao2_iterator_init(cnf->usercontainer, 0);
1750 for (; (usr = ao2_iterator_next(&iter)); ao2_ref(usr, -1)) {
1751 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
1752 if (!strncmp(word, usrno, len) && ++which > state) {
1754 ret = ast_strdup(usrno);
1758 ao2_iterator_destroy(&iter);
1762 static char *complete_meetmecmd_mute_kick(const char *line, const char *word, int pos, int state)
1765 return complete_confno(word, state);
1768 int len = strlen(word);
1773 struct ast_conference *cnf;
1775 if (!strncasecmp(word, "all", len)) {
1777 return ast_strdup("all");
1782 /* Extract the confno from the command line. */
1783 myline = ast_strdupa(line);
1784 strtok_r(myline, " ", &saved);
1785 strtok_r(NULL, " ", &saved);
1786 confno = strtok_r(NULL, " ", &saved);
1788 AST_LIST_LOCK(&confs);
1789 AST_LIST_TRAVERSE(&confs, cnf, list) {
1790 if (!strcmp(confno, cnf->confno)) {
1791 ret = complete_userno(cnf, word, state);
1795 AST_LIST_UNLOCK(&confs);
1802 static char *complete_meetmecmd_lock(const char *word, int pos, int state)
1805 return complete_confno(word, state);
1810 static char *complete_meetmecmd_list(const char *line, const char *word, int pos, int state)
1816 if (!strncasecmp(word, STR_CONCISE, len)) {
1818 return ast_strdup(STR_CONCISE);
1823 return complete_confno(word, state);
1825 if (pos == 3 && state == 0) {
1830 /* Extract the confno from the command line. */
1831 myline = ast_strdupa(line);
1832 strtok_r(myline, " ", &saved);
1833 strtok_r(NULL, " ", &saved);
1834 confno = strtok_r(NULL, " ", &saved);
1836 if (!strcasecmp(confno, STR_CONCISE)) {
1837 /* There is nothing valid in this position now. */
1842 if (!strncasecmp(word, STR_CONCISE, len)) {
1843 return ast_strdup(STR_CONCISE);
1849 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1851 /* Process the command */
1852 struct ast_conf_user *user;
1853 struct ast_conference *cnf;
1857 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
1858 #define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
1862 e->command = "meetme list";
1864 "Usage: meetme list [<confno>] [" STR_CONCISE "]\n"
1865 " List all conferences or a specific conference.\n";
1868 return complete_meetmecmd_list(a->line, a->word, a->pos, a->n);
1871 if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], STR_CONCISE))) {
1872 /* List all the conferences */
1873 int concise = (a->argc == 3);
1874 struct ast_str *marked_users;
1876 if (!(marked_users = ast_str_create(30))) {
1881 AST_LIST_LOCK(&confs);
1882 if (AST_LIST_EMPTY(&confs)) {
1884 ast_cli(a->fd, "No active MeetMe conferences.\n");
1886 AST_LIST_UNLOCK(&confs);
1887 ast_free(marked_users);
1891 ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
1893 AST_LIST_TRAVERSE(&confs, cnf, list) {
1894 hr = (now - cnf->start) / 3600;
1895 min = ((now - cnf->start) % 3600) / 60;
1896 sec = (now - cnf->start) % 60;
1898 if (cnf->markedusers == 0) {
1899 ast_str_set(&marked_users, 0, "N/A ");
1901 ast_str_set(&marked_users, 0, "%4.4d", cnf->markedusers);
1903 ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users,
1904 ast_str_buffer(marked_users), hr, min, sec,
1905 cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
1907 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1916 total += cnf->users;
1918 AST_LIST_UNLOCK(&confs);
1920 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1922 ast_free(marked_users);
1925 if (a->argc == 3 || (a->argc == 4 && !strcasecmp(a->argv[3], STR_CONCISE))) {
1926 struct ao2_iterator user_iter;
1927 int concise = (a->argc == 4);
1929 /* List all the users in a conference */
1930 if (AST_LIST_EMPTY(&confs)) {
1932 ast_cli(a->fd, "No active MeetMe conferences.\n");
1936 /* Find the right conference */
1937 AST_LIST_LOCK(&confs);
1938 AST_LIST_TRAVERSE(&confs, cnf, list) {
1939 if (strcmp(cnf->confno, a->argv[2]) == 0) {
1945 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
1946 AST_LIST_UNLOCK(&confs);
1949 /* Show all the users */
1951 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
1952 while((user = ao2_iterator_next(&user_iter))) {
1953 hr = (now - user->jointime) / 3600;
1954 min = ((now - user->jointime) % 3600) / 60;
1955 sec = (now - user->jointime) % 60;
1957 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1959 S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
1960 S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<no name>"),
1961 ast_channel_name(user->chan),
1962 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "(Admin)" : "",
1963 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "(Listen only)" : "",
1964 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1965 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1966 istalking(user->talking), hr, min, sec);
1968 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1970 S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, ""),
1971 S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, ""),
1972 ast_channel_name(user->chan),
1973 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "1" : "",
1974 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "1" : "",
1975 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1976 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1977 user->talking, hr, min, sec);
1981 ao2_iterator_destroy(&user_iter);
1983 ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
1985 AST_LIST_UNLOCK(&confs);
1988 return CLI_SHOWUSAGE;
1992 static char *meetme_cmd_helper(struct ast_cli_args *a)
1994 /* Process the command */
1995 struct ast_str *cmdline;
1997 /* Max confno length */
1998 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
2002 ast_str_set(&cmdline, 0, "%s", a->argv[2]); /* Argv 2: conference number */
2003 if (strcasestr(a->argv[1], "lock")) {
2004 if (strcasecmp(a->argv[1], "lock") == 0) {
2006 ast_str_append(&cmdline, 0, ",L");
2009 ast_str_append(&cmdline, 0, ",l");
2011 } else if (strcasestr(a->argv[1], "mute")) {
2012 if (strcasecmp(a->argv[1], "mute") == 0) {
2014 if (strcasecmp(a->argv[3], "all") == 0) {
2015 ast_str_append(&cmdline, 0, ",N");
2017 ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);
2021 if (strcasecmp(a->argv[3], "all") == 0) {
2022 ast_str_append(&cmdline, 0, ",n");
2024 ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
2027 } else if (strcasecmp(a->argv[1], "kick") == 0) {
2028 if (strcasecmp(a->argv[3], "all") == 0) {
2030 ast_str_append(&cmdline, 0, ",K");
2032 /* Kick a single user */
2033 ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
2037 * Should never get here because it is already filtered by the
2041 return CLI_SHOWUSAGE;
2044 ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
2046 admin_exec(NULL, ast_str_buffer(cmdline));
2052 static char *meetme_lock_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2056 e->command = "meetme {lock|unlock}";
2058 "Usage: meetme lock|unlock <confno>\n"
2059 " Lock or unlock a conference to new users.\n";
2062 return complete_meetmecmd_lock(a->word, a->pos, a->n);
2066 return CLI_SHOWUSAGE;
2069 return meetme_cmd_helper(a);
2072 static char *meetme_kick_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2076 e->command = "meetme kick";
2078 "Usage: meetme kick <confno> all|<userno>\n"
2079 " Kick a conference or a user in a conference.\n";
2082 return complete_meetmecmd_mute_kick(a->line, a->word, a->pos, a->n);
2086 return CLI_SHOWUSAGE;
2089 return meetme_cmd_helper(a);
2092 static char *meetme_mute_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2096 e->command = "meetme {mute|unmute}";
2098 "Usage: meetme mute|unmute <confno> all|<userno>\n"
2099 " Mute or unmute a conference or a user in a conference.\n";
2102 return complete_meetmecmd_mute_kick(a->line, a->word, a->pos, a->n);
2106 return CLI_SHOWUSAGE;
2109 return meetme_cmd_helper(a);
2112 static const char *sla_hold_str(unsigned int hold_access)
2114 const char *hold = "Unknown";
2116 switch (hold_access) {
2120 case SLA_HOLD_PRIVATE:
2129 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2131 struct ao2_iterator i;
2132 struct sla_trunk *trunk;
2136 e->command = "sla show trunks";
2138 "Usage: sla show trunks\n"
2139 " This will list all trunks defined in sla.conf\n";
2146 "=============================================================\n"
2147 "=== Configured SLA Trunks ===================================\n"
2148 "=============================================================\n"
2150 i = ao2_iterator_init(sla_trunks, 0);
2151 for (; (trunk = ao2_iterator_next(&i)); ao2_ref(trunk, -1)) {
2152 struct sla_station_ref *station_ref;
2153 char ring_timeout[16] = "(none)";
2157 if (trunk->ring_timeout) {
2158 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
2161 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
2162 "=== Trunk Name: %s\n"
2163 "=== ==> Device: %s\n"
2164 "=== ==> AutoContext: %s\n"
2165 "=== ==> RingTimeout: %s\n"
2166 "=== ==> BargeAllowed: %s\n"
2167 "=== ==> HoldAccess: %s\n"
2168 "=== ==> Stations ...\n",
2169 trunk->name, trunk->device,
2170 S_OR(trunk->autocontext, "(none)"),
2172 trunk->barge_disabled ? "No" : "Yes",
2173 sla_hold_str(trunk->hold_access));
2175 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
2176 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
2179 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
2183 ao2_iterator_destroy(&i);
2184 ast_cli(a->fd, "=============================================================\n\n");
2189 static const char *trunkstate2str(enum sla_trunk_state state)
2191 #define S(e) case e: return # e;
2193 S(SLA_TRUNK_STATE_IDLE)
2194 S(SLA_TRUNK_STATE_RINGING)
2195 S(SLA_TRUNK_STATE_UP)
2196 S(SLA_TRUNK_STATE_ONHOLD)
2197 S(SLA_TRUNK_STATE_ONHOLD_BYME)
2199 return "Uknown State";
2203 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2205 struct ao2_iterator i;
2206 struct sla_station *station;
2210 e->command = "sla show stations";
2212 "Usage: sla show stations\n"
2213 " This will list all stations defined in sla.conf\n";
2220 "=============================================================\n"
2221 "=== Configured SLA Stations =================================\n"
2222 "=============================================================\n"
2224 i = ao2_iterator_init(sla_stations, 0);
2225 for (; (station = ao2_iterator_next(&i)); ao2_ref(station, -1)) {
2226 struct sla_trunk_ref *trunk_ref;
2227 char ring_timeout[16] = "(none)";
2228 char ring_delay[16] = "(none)";
2232 if (station->ring_timeout) {
2233 snprintf(ring_timeout, sizeof(ring_timeout),
2234 "%u", station->ring_timeout);
2236 if (station->ring_delay) {
2237 snprintf(ring_delay, sizeof(ring_delay),
2238 "%u", station->ring_delay);
2240 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
2241 "=== Station Name: %s\n"
2242 "=== ==> Device: %s\n"
2243 "=== ==> AutoContext: %s\n"
2244 "=== ==> RingTimeout: %s\n"
2245 "=== ==> RingDelay: %s\n"
2246 "=== ==> HoldAccess: %s\n"
2247 "=== ==> Trunks ...\n",
2248 station->name, station->device,
2249 S_OR(station->autocontext, "(none)"),
2250 ring_timeout, ring_delay,
2251 sla_hold_str(station->hold_access));
2252 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
2253 if (trunk_ref->ring_timeout) {
2254 snprintf(ring_timeout, sizeof(ring_timeout),
2255 "%u", trunk_ref->ring_timeout);
2257 strcpy(ring_timeout, "(none)");
2258 if (trunk_ref->ring_delay) {
2259 snprintf(ring_delay, sizeof(ring_delay),
2260 "%u", trunk_ref->ring_delay);
2262 strcpy(ring_delay, "(none)");
2263 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
2264 "=== ==> State: %s\n"
2265 "=== ==> RingTimeout: %s\n"
2266 "=== ==> RingDelay: %s\n",
2267 trunk_ref->trunk->name,
2268 trunkstate2str(trunk_ref->state),
2269 ring_timeout, ring_delay);
2271 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
2274 ao2_unlock(station);
2276 ao2_iterator_destroy(&i);
2277 ast_cli(a->fd, "============================================================\n"
2283 static struct ast_cli_entry cli_meetme[] = {
2284 AST_CLI_DEFINE(meetme_kick_cmd, "Kick a conference or a user in a conference."),
2285 AST_CLI_DEFINE(meetme_show_cmd, "List all conferences or a specific conference."),
2286 AST_CLI_DEFINE(meetme_lock_cmd, "Lock or unlock a conference to new users."),
2287 AST_CLI_DEFINE(meetme_mute_cmd, "Mute or unmute a conference or a user in a conference."),
2288 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
2289 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
2292 static void conf_flush(int fd, struct ast_channel *chan)
2296 /* read any frames that may be waiting on the channel
2300 struct ast_frame *f;
2302 /* when no frames are available, this will wait
2303 for 1 millisecond maximum
2305 while (ast_waitfor(chan, 1) > 0) {
2309 else /* channel was hung up or something else happened */
2314 /* flush any data sitting in the pseudo channel */
2315 x = DAHDI_FLUSH_ALL;
2316 if (ioctl(fd, DAHDI_FLUSH, &x))
2317 ast_log(LOG_WARNING, "Error flushing channel\n");
2321 /*! \brief Remove the conference from the list and free it.
2323 We assume that this was called while holding conflock. */
2324 static int conf_free(struct ast_conference *conf)
2327 struct announce_listitem *item;
2329 AST_LIST_REMOVE(&confs, conf, list);
2331 meetme_stasis_generate_msg(conf, NULL, NULL, meetme_end_type(), NULL);
2333 if (conf->recording == MEETME_RECORD_ACTIVE) {
2334 conf->recording = MEETME_RECORD_TERMINATE;
2335 AST_LIST_UNLOCK(&confs);
2338 AST_LIST_LOCK(&confs);
2339 if (conf->recording == MEETME_RECORD_OFF)
2341 AST_LIST_UNLOCK(&confs);
2345 for (x = 0; x < AST_FRAME_BITS; x++) {
2346 if (conf->transframe[x])
2347 ast_frfree(conf->transframe[x]);
2348 if (conf->transpath[x])
2349 ast_translator_free_path(conf->transpath[x]);
2351 if (conf->announcethread != AST_PTHREADT_NULL) {
2352 ast_mutex_lock(&conf->announcelistlock);
2353 conf->announcethread_stop = 1;
2354 ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
2355 ast_cond_signal(&conf->announcelist_addition);
2356 ast_mutex_unlock(&conf->announcelistlock);
2357 pthread_join(conf->announcethread, NULL);
2359 while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
2360 /* If it's a voicemail greeting file we don't want to remove it */
2362 ast_filedelete(item->namerecloc, NULL);
2366 ast_mutex_destroy(&conf->announcelistlock);
2369 if (conf->origframe)
2370 ast_frfree(conf->origframe);
2371 ast_hangup(conf->lchan);
2372 ast_hangup(conf->chan);
2375 if (conf->recordingfilename) {
2376 ast_free(conf->recordingfilename);
2378 if (conf->usercontainer) {
2379 ao2_ref(conf->usercontainer, -1);
2381 if (conf->recordingformat) {
2382 ast_free(conf->recordingformat);
2384 ast_mutex_destroy(&conf->playlock);
2385 ast_mutex_destroy(&conf->listenlock);
2386 ast_mutex_destroy(&conf->recordthreadlock);
2387 ast_mutex_destroy(&conf->announcethreadlock);
2393 static void conf_queue_dtmf(const struct ast_conference *conf,
2394 const struct ast_conf_user *sender, struct ast_frame *f)
2396 struct ast_conf_user *user;
2397 struct ao2_iterator user_iter;
2399 user_iter = ao2_iterator_init(conf->usercontainer, 0);
2400 while ((user = ao2_iterator_next(&user_iter))) {
2401 if (user == sender) {
2405 if (ast_write(user->chan, f) < 0)
2406 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", ast_channel_name(user->chan));
2409 ao2_iterator_destroy(&user_iter);
2412 static void sla_queue_event_full(enum sla_event_type type,
2413 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
2415 struct sla_event *event;
2417 if (sla.thread == AST_PTHREADT_NULL) {
2418 ao2_ref(station, -1);
2419 ao2_ref(trunk_ref, -1);
2423 if (!(event = ast_calloc(1, sizeof(*event)))) {
2424 ao2_ref(station, -1);
2425 ao2_ref(trunk_ref, -1);
2430 event->trunk_ref = trunk_ref;
2431 event->station = station;
2434 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
2438 ast_mutex_lock(&sla.lock);
2439 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
2440 ast_cond_signal(&sla.cond);
2441 ast_mutex_unlock(&sla.lock);
2444 static void sla_queue_event_nolock(enum sla_event_type type)
2446 sla_queue_event_full(type, NULL, NULL, 0);
2449 static void sla_queue_event(enum sla_event_type type)
2451 sla_queue_event_full(type, NULL, NULL, 1);
2454 /*! \brief Queue a SLA event from the conference */
2455 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
2456 struct ast_conference *conf)
2458 struct sla_station *station;
2459 struct sla_trunk_ref *trunk_ref = NULL;
2461 struct ao2_iterator i;
2463 trunk_name = ast_strdupa(conf->confno);
2464 strsep(&trunk_name, "_");
2465 if (ast_strlen_zero(trunk_name)) {
2466 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
2470 i = ao2_iterator_init(sla_stations, 0);
2471 while ((station = ao2_iterator_next(&i))) {
2473 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
2474 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name)) {
2475 ao2_ref(trunk_ref, 1);
2479 ao2_unlock(station);
2481 /* station reference given to sla_queue_event_full() */
2484 ao2_ref(station, -1);
2486 ao2_iterator_destroy(&i);
2489 ast_debug(1, "Trunk not found for event!\n");
2493 sla_queue_event_full(type, trunk_ref, station, 1);
2496 /*! \brief Decrement reference counts, as incremented by find_conf() */
2497 static int dispose_conf(struct ast_conference *conf)
2502 AST_LIST_LOCK(&confs);
2503 if (ast_atomic_dec_and_test(&conf->refcount)) {
2504 /* Take the conference room number out of an inuse state */
2505 if ((sscanf(conf->confno, "%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
2506 conf_map[confno_int] = 0;
2511 AST_LIST_UNLOCK(&confs);
2516 static int rt_extend_conf(const char *confno)
2518 char currenttime[32];
2522 struct ast_variable *var, *orig_var;
2531 ast_localtime(&now, &tm, NULL);
2532 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2534 var = ast_load_realtime("meetme", "confno",
2535 confno, "startTime<= ", currenttime,
2536 "endtime>= ", currenttime, NULL);
2540 /* Identify the specific RealTime conference */
2542 if (!strcasecmp(var->name, "bookid")) {
2543 ast_copy_string(bookid, var->value, sizeof(bookid));
2545 if (!strcasecmp(var->name, "endtime")) {
2546 ast_copy_string(endtime, var->value, sizeof(endtime));
2551 ast_variables_destroy(orig_var);
2553 ast_strptime(endtime, DATE_FORMAT, &tm);
2554 now = ast_mktime(&tm, NULL);
2556 now.tv_sec += extendby;
2558 ast_localtime(&now, &tm, NULL);
2559 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2560 strcat(currenttime, "0"); /* Seconds needs to be 00 */
2562 var = ast_load_realtime("meetme", "confno",
2563 confno, "startTime<= ", currenttime,
2564 "endtime>= ", currenttime, NULL);
2566 /* If there is no conflict with extending the conference, update the DB */
2568 ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
2569 ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
2574 ast_variables_destroy(var);
2578 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
2582 ast_channel_lock(chan);
2583 original_moh = ast_strdupa(ast_channel_musicclass(chan));
2584 ast_channel_musicclass_set(chan, musicclass);
2585 ast_channel_unlock(chan);
2587 ast_moh_start(chan, original_moh, NULL);
2589 ast_channel_lock(chan);
2590 ast_channel_musicclass_set(chan, original_moh);
2591 ast_channel_unlock(chan);
2594 static const char *get_announce_filename(enum announcetypes type)
2598 return "conf-hasleft";
2601 return "conf-hasjoin";
2608 static void *announce_thread(void *data)
2610 struct announce_listitem *current;
2611 struct ast_conference *conf = data;
2613 char filename[PATH_MAX] = "";
2614 AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
2615 AST_LIST_HEAD_INIT_NOLOCK(&local_list);
2617 while (!conf->announcethread_stop) {
2618 ast_mutex_lock(&conf->announcelistlock);
2619 if (conf->announcethread_stop) {
2620 ast_mutex_unlock(&conf->announcelistlock);
2623 if (AST_LIST_EMPTY(&conf->announcelist))
2624 ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
2626 AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
2627 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2629 ast_mutex_unlock(&conf->announcelistlock);
2630 if (conf->announcethread_stop) {
2634 for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
2635 ast_debug(1, "About to play %s\n", current->namerecloc);
2636 if (!ast_fileexists(current->namerecloc, NULL, NULL))
2638 if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
2639 if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
2640 res = ast_waitstream(current->confchan, "");
2642 ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
2643 if (!ast_streamfile(current->confchan, filename, current->language))
2644 ast_waitstream(current->confchan, "");
2647 if (current->announcetype == CONF_HASLEFT && current->announcetype && !current->vmrec) {
2648 /* only remove it if it isn't a VM recording file */
2649 ast_filedelete(current->namerecloc, NULL);
2654 /* thread marked to stop, clean up */
2655 while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
2656 /* only delete if it's a vm rec */
2657 if (!current->vmrec) {
2658 ast_filedelete(current->namerecloc, NULL);
2660 ao2_ref(current, -1);
2665 static int can_write(struct ast_channel *chan, struct ast_flags64 *confflags)
2667 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
2671 return (ast_channel_state(chan) == AST_STATE_UP);
2674 static void send_talking_event(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking)
2676 RAII_VAR(struct ast_json *, status_blob, status_to_json(talking), ast_json_unref);
2677 meetme_stasis_generate_msg(conf, chan, user, meetme_talking_type(), status_blob);
2680 static void set_user_talking(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor)
2682 int last_talking = user->talking;
2683 if (last_talking == talking)
2686 user->talking = talking;
2689 /* Check if talking state changed. Take care of -1 which means unmonitored */
2690 int was_talking = (last_talking > 0);
2691 int now_talking = (talking > 0);
2692 if (was_talking != now_talking) {
2693 send_talking_event(chan, conf, user, now_talking);
2698 static int user_set_hangup_cb(void *obj, void *check_admin_arg, int flags)
2700 struct ast_conf_user *user = obj;
2701 /* actual pointer contents of check_admin_arg is irrelevant */
2703 if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
2704 user->adminflags |= ADMINFLAG_HANGUP;
2709 static int user_set_kickme_cb(void *obj, void *check_admin_arg, int flags)
2711 struct ast_conf_user *user = obj;
2712 /* actual pointer contents of check_admin_arg is irrelevant */
2714 if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
2715 user->adminflags |= ADMINFLAG_KICKME;
2720 static int user_set_unmuted_cb(void *obj, void *check_admin_arg, int flags)
2722 struct ast_conf_user *user = obj;
2723 /* actual pointer contents of check_admin_arg is irrelevant */
2725 if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
2726 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
2731 static int user_set_muted_cb(void *obj, void *check_admin_arg, int flags)
2733 struct ast_conf_user *user = obj;
2734 /* actual pointer contents of check_admin_arg is irrelevant */
2736 if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
2737 user->adminflags |= ADMINFLAG_MUTED;
2746 MENU_ADMIN_EXTENDED,
2750 * \brief Processes menu options for the standard menu (accessible through the 's' option for app_meetme)
2752 * \param menu_mode a pointer to the currently active menu_mode.
2753 * \param dtmf a pointer to the dtmf value currently being processed against the menu.
2754 * \param conf the active conference for which the user has called the menu from.
2755 * \param confflags flags used by conf for various options
2756 * \param chan ast_channel belonging to the user who called the menu
2757 * \param user which meetme conference user invoked the menu
2759 static void meetme_menu_normal(enum menu_modes *menu_mode, int *dtmf, struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, struct ast_conf_user *user)
2762 case '1': /* Un/Mute */
2763 *menu_mode = MENU_DISABLED;
2765 /* user can only toggle the self-muted state */
2766 user->adminflags ^= ADMINFLAG_SELFMUTED;
2768 /* they can't override the admin mute state */
2769 if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2770 if (!ast_streamfile(chan, "conf-muted", ast_channel_language(chan))) {
2771 ast_waitstream(chan, "");
2774 if (!ast_streamfile(chan, "conf-unmuted", ast_channel_language(chan))) {
2775 ast_waitstream(chan, "");
2781 *menu_mode = MENU_DISABLED;
2782 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
2783 user->adminflags |= ADMINFLAG_T_REQUEST;
2786 if (user->adminflags & ADMINFLAG_T_REQUEST) {
2787 if (!ast_streamfile(chan, "beep", ast_channel_language(chan))) {
2788 ast_waitstream(chan, "");
2794 tweak_listen_volume(user, VOL_DOWN);
2797 /* Extend RT conference */
2799 rt_extend_conf(conf->confno);
2801 *menu_mode = MENU_DISABLED;
2805 tweak_listen_volume(user, VOL_UP);
2809 tweak_talk_volume(user, VOL_DOWN);
2813 *menu_mode = MENU_DISABLED;
2817 tweak_talk_volume(user, VOL_UP);
2821 *menu_mode = MENU_DISABLED;
2822 if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
2823 ast_waitstream(chan, "");
2830 * \brief Processes menu options for the adminstrator menu (accessible through the 's' option for app_meetme)
2832 * \param menu_mode a pointer to the currently active menu_mode.
2833 * \param dtmf a pointer to the dtmf value currently being processed against the menu.
2834 * \param conf the active conference for which the user has called the menu from.
2835 * \param confflags flags used by conf for various options
2836 * \param chan ast_channel belonging to the user who called the menu
2837 * \param user which meetme conference user invoked the menu
2839 static void meetme_menu_admin(enum menu_modes *menu_mode, int *dtmf, struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, struct ast_conf_user *user)
2842 case '1': /* Un/Mute */
2843 *menu_mode = MENU_DISABLED;
2844 /* for admin, change both admin and use flags */
2845 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
2846 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2848 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2851 if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2852 if (!ast_streamfile(chan, "conf-muted", ast_channel_language(chan))) {
2853 ast_waitstream(chan, "");
2856 if (!ast_streamfile(chan, "conf-unmuted", ast_channel_language(chan))) {
2857 ast_waitstream(chan, "");
2862 case '2': /* Un/Lock the Conference */
2863 *menu_mode = MENU_DISABLED;
2866 if (!ast_streamfile(chan, "conf-unlockednow", ast_channel_language(chan))) {
2867 ast_waitstream(chan, "");
2871 if (!ast_streamfile(chan, "conf-lockednow", ast_channel_language(chan))) {
2872 ast_waitstream(chan, "");
2877 case '3': /* Eject last user */
2879 struct ast_conf_user *usr = NULL;
2881 ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
2882 *menu_mode = MENU_DISABLED;
2883 usr = ao2_find(conf->usercontainer, &max_no, 0);
2884 if ((ast_channel_name(usr->chan) == ast_channel_name(chan)) || ast_test_flag64(&usr->userflags, CONFFLAG_ADMIN)) {
2885 if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
2886 ast_waitstream(chan, "");
2889 usr->adminflags |= ADMINFLAG_KICKME;
2892 ast_stopstream(chan);
2897 tweak_listen_volume(user, VOL_DOWN);
2901 /* Extend RT conference */
2903 if (!rt_extend_conf(conf->confno)) {
2904 if (!ast_streamfile(chan, "conf-extended", ast_channel_language(chan))) {
2905 ast_waitstream(chan, "");
2908 if (!ast_streamfile(chan, "conf-nonextended", ast_channel_language(chan))) {
2909 ast_waitstream(chan, "");
2912 ast_stopstream(chan);
2914 *menu_mode = MENU_DISABLED;
2918 tweak_listen_volume(user, VOL_UP);
2922 tweak_talk_volume(user, VOL_DOWN);
2926 if (!ast_streamfile(chan, "conf-adminmenu-menu8", ast_channel_language(chan))) {
2927 /* If the user provides DTMF while playing the sound, we want to drop right into the extended menu function with new DTMF once we get out of here. */
2928 *dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2929 ast_stopstream(chan);
2931 *menu_mode = MENU_ADMIN_EXTENDED;
2935 tweak_talk_volume(user, VOL_UP);
2938 menu_mode = MENU_DISABLED;
2939 /* Play an error message! */
2940 if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
2941 ast_waitstream(chan, "");
2949 * \brief Processes menu options for the extended administrator menu (accessible through option 8 on the administrator menu)
2951 * \param menu_mode a pointer to the currently active menu_mode.
2952 * \param dtmf a pointer to the dtmf value currently being processed against the menu.
2953 * \param conf the active conference for which the user has called the menu from.
2954 * \param confflags flags used by conf for various options
2955 * \param chan ast_channel belonging to the user who called the menu
2956 * \param user which meetme conference user invoked the menu
2957 * \param recordingtmp character buffer which may hold the name of the conference recording file
2958 * \param dahdic dahdi configuration info used by the main conference loop
2960 static void meetme_menu_admin_extended(enum menu_modes *menu_mode, int *dtmf, struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, struct ast_conf_user *user, char *recordingtmp, struct dahdi_confinfo *dahdic, struct ast_format_cap *cap_slin)
2965 struct ao2_iterator user_iter;
2966 struct ast_conf_user *usr = NULL;
2969 case '1': /* *81 Roll call */
2972 if (conf->users == 1) {
2973 if (keepplaying && !ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan))) {
2974 res = ast_waitstream(chan, AST_DIGIT_ANY);
2975 ast_stopstream(chan);
2980 } else if (conf->users == 2) {
2981 if (keepplaying && !ast_streamfile(chan, "conf-onlyone", ast_channel_language(chan))) {
2982 res = ast_waitstream(chan, AST_DIGIT_ANY);
2983 ast_stopstream(chan);
2989 if (keepplaying && !ast_streamfile(chan, "conf-thereare", ast_channel_language(chan))) {
2990 res = ast_waitstream(chan, AST_DIGIT_ANY);
2991 ast_stopstream(chan);
2997 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
2998 ast_stopstream(chan);
3003 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", ast_channel_language(chan))) {
3004 res = ast_waitstream(chan, AST_DIGIT_ANY);
3005 ast_stopstream(chan);
3011 user_iter = ao2_iterator_init(conf->usercontainer, 0);
3012 while((usr = ao2_iterator_next(&user_iter))) {
3013 if (ast_fileexists(usr->namerecloc, NULL, NULL)) {
3014 if (keepplaying && !ast_streamfile(chan, usr->namerecloc, ast_channel_language(chan))) {
3015 res = ast_waitstream(chan, AST_DIGIT_ANY);
3016 ast_stopstream(chan);
3025 ao2_iterator_destroy(&user_iter);
3026 if (keepplaying && playednamerec && !ast_streamfile(chan, "conf-roll-callcomplete", ast_channel_language(chan))) {
3027 res = ast_waitstream(chan, AST_DIGIT_ANY);
3028 ast_stopstream(chan);
3034 *menu_mode = MENU_DISABLED;
3037 case '2': /* *82 Eject all non-admins */
3038 if (conf->users == 1) {
3039 if(!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
3040 ast_waitstream(chan, "");
3043 ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_kickme_cb, &conf);
3045 ast_stopstream(chan);
3046 *menu_mode = MENU_DISABLED;
3049 case '3': /* *83 (Admin) mute/unmute all non-admins */
3052 ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, &conf);
3053 if (!ast_streamfile(chan, "conf-now-unmuted", ast_channel_language(chan))) {
3054 ast_waitstream(chan, "");
3058 ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_muted_cb, &conf);
3059 if (!ast_streamfile(chan, "conf-now-muted", ast_channel_language(chan))) {
3060 ast_waitstream(chan, "");
3063 ast_stopstream(chan);
3064 *menu_mode = MENU_DISABLED;
3067 case '4': /* *84 Record conference */
3068 if (conf->recording != MEETME_RECORD_ACTIVE) {
3069 ast_set_flag64(confflags, CONFFLAG_RECORDCONF);
3070 if (!conf->recordingfilename) {
3072 ast_channel_lock(chan);
3073 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
3074 conf->recordingfilename = ast_strdup(var);
3076 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
3077 conf->recordingformat = ast_strdup(var);
3079 ast_channel_unlock(chan);
3080 if (!conf->recordingfilename) {
3081 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan));
3082 conf->recordingfilename = ast_strdup(recordingtmp);
3084 if (!conf->recordingformat) {
3085 conf->recordingformat = ast_strdup("wav");
3087 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
3088 conf->confno, conf->recordingfilename, conf->recordingformat);
3091 ast_mutex_lock(&conf->recordthreadlock);
3092 if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL)))) {
3093 ast_set_read_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
3094 ast_set_write_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
3096 dahdic->confno = conf->dahdiconf;
3097 dahdic->confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
3098 if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, dahdic)) {
3099 ast_log(LOG_WARNING, "Error starting listen channel\n");
3100 ast_hangup(conf->lchan);
3103 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
3106 ast_mutex_unlock(&conf->recordthreadlock);
3107 if (!ast_streamfile(chan, "conf-now-recording", ast_channel_language(chan))) {
3108 ast_waitstream(chan, "");
3112 ast_stopstream(chan);
3113 *menu_mode = MENU_DISABLED;
3116 case '8': /* *88 Exit the menu and return to the conference... without an error message */
3117 ast_stopstream(chan);
3118 *menu_mode = MENU_DISABLED;
3122 if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
3123 ast_waitstream(chan, "");
3125 ast_stopstream(chan);
3126 *menu_mode = MENU_DISABLED;
3132 * \brief Processes menu options for the various menu types (accessible through the 's' option for app_meetme)
3134 * \param menu_mode a pointer to the currently active menu_mode.
3135 * \param dtmf a pointer to the dtmf value currently being processed against the menu.
3136 * \param conf the active conference for which the user has called the menu from.
3137 * \param confflags flags used by conf for various options
3138 * \param chan ast_channel belonging to the user who called the menu
3139 * \param user which meetme conference user invoked the menu
3140 * \param recordingtmp character buffer which may hold the name of the conference recording file
3141 * \param dahdic dahdi configuration info used by the main conference loop
3144 static void meetme_menu(enum menu_modes *menu_mode, int *dtmf, struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, struct ast_conf_user *user, char *recordingtmp, struct dahdi_confinfo *dahdic, struct ast_format_cap *cap_slin)
3146 switch (*menu_mode) {
3150 meetme_menu_normal(menu_mode, dtmf, conf, confflags, chan, user);
3153 meetme_menu_admin(menu_mode, dtmf, conf, confflags, chan, user);
3154 /* Admin Menu is capable of branching into another menu, in which case it will reset dtmf and change the menu mode. */
3155 if (*menu_mode != MENU_ADMIN_EXTENDED || (*dtmf <= 0)) {
3158 case MENU_ADMIN_EXTENDED:
3159 meetme_menu_admin_extended(menu_mode, dtmf, conf, confflags, chan, user, recordingtmp, dahdic, cap_slin);
3164 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struct ast_flags64 *confflags, char *optargs[])
3166 struct ast_conf_user *user = NULL;
3168 struct dahdi_confinfo dahdic, dahdic_empty;
3169 struct ast_frame *f;
3170 struct ast_channel *c;
3171 struct ast_frame fr;
3178 int musiconhold = 0, mohtempstopped = 0;
3181 int currentmarked = 0;
3184 enum menu_modes menu_mode = MENU_DISABLED;
3185 int talkreq_manager = 0;
3186 int using_pseudo = 0;
3190 int announcement_played = 0;
3192 struct ast_dsp *dsp = NULL;
3193 struct ast_app *agi_app;
3194 char *agifile, *mod_speex;
3195 const char *agifiledefault = "conf-background.agi", *tmpvar;
3196 char meetmesecs[30] = "";
3197 char exitcontext[AST_MAX_CONTEXT] = "";
3198 char recordingtmp[AST_MAX_EXTENSION] = "";
3199 char members[10] = "";
3200 int dtmf = 0, opt_waitmarked_timeout = 0;
3202 struct dahdi_bufferinfo bi;
3203 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
3204 char *buf = __buf + AST_FRIENDLY_OFFSET;
3205 char *exitkeys = NULL;
3206 unsigned int calldurationlimit = 0;
3208 long play_warning = 0;
3209 long warning_freq = 0;
3210 const char *warning_sound = NULL;
3211 const char *end_sound = NULL;
3213 long time_left_ms = 0;
3214 struct timeval nexteventts = { 0, };
3216 int setusercount = 0;
3217 int confsilence = 0, totalsilence = 0;
3218 char *mailbox, *context;
3219 struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
3220 struct ast_format tmpfmt;
3223 goto conf_run_cleanup;
3225 ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
3227 if (!(user = ao2_alloc(sizeof(*user), NULL))) {
3228 goto conf_run_cleanup;
3231 /* Possible timeout waiting for marked user */
3232 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
3233 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
3234 (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
3235 (opt_waitmarked_timeout > 0)) {
3236 timeout = time(NULL) + opt_waitmarked_timeout;
3239 if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
3240 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
3241 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
3244 if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
3245 char *limit_str, *warning_str, *warnfreq_str;
3248 parse = optargs[OPT_ARG_DURATION_LIMIT];
3249 limit_str = strsep(&parse, ":");
3250 warning_str = strsep(&parse, ":");
3251 warnfreq_str = parse;
3253 timelimit = atol(limit_str);
3255 play_warning = atol(warning_str);
3257 warning_freq = atol(warnfreq_str);
3260 timelimit = play_warning = warning_freq = 0;
3261 warning_sound = NULL;
3262 } else if (play_warning > timelimit) {
3263 if (!warning_freq) {
3266 while (play_warning > timelimit)
3267 play_warning -= warning_freq;
3268 if (play_warning < 1)
3269 play_warning = warning_freq = 0;
3273 ast_verb(3, "Setting conference duration limit to: %ldms.\n", timelimit);
3275 ast_verb(3, "Setting warning time to %ldms from the conference duration limit.\n", play_warning);
3278 ast_verb(3, "Setting warning frequency to %ldms.\n", warning_freq);
3281 ast_channel_lock(chan);
3282 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
3283 var = ast_strdupa(var);
3285 ast_channel_unlock(chan);
3287 warning_sound = var ? var : "timeleft";
3289 ast_channel_lock(chan);
3290 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
3291 var = ast_strdupa(var);
3293 ast_channel_unlock(chan);
3295 end_sound = var ? var : NULL;
3297 /* undo effect of S(x) in case they are both used */
3298 calldurationlimit = 0;
3299 /* more efficient do it like S(x) does since no advanced opts */
3300 if (!play_warning && !end_sound && timelimit) {
3301 calldurationlimit = timelimit / 1000;
3302 timelimit = play_warning = warning_freq = 0;
3304 ast_debug(2, "Limit Data for this call:\n");
3305 ast_debug(2, "- timelimit = %ld\n", timelimit);
3306 ast_debug(2, "- play_warning = %ld\n", play_warning);
3307 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
3308 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
3309 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
3314 if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
3315 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
3316 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
3318 exitkeys = ast_strdupa("#"); /* Default */
3321 if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
3322 if (!conf->recordingfilename) {
3324 ast_channel_lock(chan);
3325 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
3326 conf->recordingfilename = ast_strdup(var);
3328 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
3329 conf->recordingformat = ast_strdup(var);
3331 ast_channel_unlock(chan);
3332 if (!conf->recordingfilename) {
3333 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan));
3334 conf->recordingfilename = ast_strdup(recordingtmp);
3336 if (!conf->recordingformat) {
3337 conf->recordingformat = ast_strdup("wav");
3339 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
3340 conf->confno, conf->recordingfilename, conf->recordingformat);
3344 ast_mutex_lock(&conf->recordthreadlock);
3345 if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
3346 ((conf->lchan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL)))) {
3347 ast_set_read_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
3348 ast_set_write_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
3350 dahdic.confno = conf->dahdiconf;
3351 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
3352 if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, &dahdic)) {
3353 ast_log(LOG_WARNING, "Error starting listen channel\n");
3354 ast_hangup(conf->lchan);
3357 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
3360 ast_mutex_unlock(&conf->recordthreadlock);
3362 ast_mutex_lock(&conf->announcethreadlock);
3363 if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
3364 ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC)) {
3365 ast_mutex_init(&conf->announcelistlock);
3366 AST_LIST_HEAD_INIT_NOLOCK(&conf->announce