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 #include <dahdi/user.h>
52 #include "asterisk/lock.h"
53 #include "asterisk/file.h"
54 #include "asterisk/channel.h"
55 #include "asterisk/pbx.h"
56 #include "asterisk/module.h"
57 #include "asterisk/config.h"
58 #include "asterisk/app.h"
59 #include "asterisk/dsp.h"
60 #include "asterisk/musiconhold.h"
61 #include "asterisk/manager.h"
62 #include "asterisk/cli.h"
63 #include "asterisk/say.h"
64 #include "asterisk/utils.h"
65 #include "asterisk/translate.h"
66 #include "asterisk/ulaw.h"
67 #include "asterisk/astobj2.h"
68 #include "asterisk/devicestate.h"
69 #include "asterisk/dial.h"
70 #include "asterisk/causes.h"
71 #include "asterisk/paths.h"
72 #include "asterisk/test.h"
73 #include "asterisk/stasis.h"
74 #include "asterisk/stasis_channels.h"
75 #include "asterisk/stasis_message_router.h"
76 #include "asterisk/json.h"
77 #include "asterisk/format_compatibility.h"
83 <application name="MeetMe" language="en_US">
85 MeetMe conference bridge.
88 <parameter name="confno">
89 <para>The conference number</para>
91 <parameter name="options">
94 <para>Set admin mode.</para>
97 <para>Set marked mode.</para>
100 <para>Run AGI script specified in <variable>MEETME_AGI_BACKGROUND</variable>
101 Default: <literal>conf-background.agi</literal>.</para>
102 <note><para>This does not work with non-DAHDI channels in the same
103 conference).</para></note>
106 <para>Announce user(s) count on joining a conference.</para>
109 <para>Continue in dialplan when kicked out of conference.</para>
112 <para>Dynamically add conference.</para>
115 <para>Dynamically add conference, prompting for a PIN.</para>
118 <para>Select an empty conference.</para>
121 <para>Select an empty pinless conference.</para>
124 <para>Pass DTMF through the conference.</para>
127 <argument name="x" required="true">
128 <para>The file to playback</para>
130 <para>Play an intro announcement in conference.</para>
133 <para>Announce user join/leave with review.</para>
136 <para>Announce user join/leave without review.</para>
139 <para>Close the conference if there's only one active participant left at exit.</para>
142 <para>Set listen only mode (Listen only, no talking).</para>
145 <para>Set initially muted.</para>
147 <option name="M" hasparams="optional">
148 <para>Enable music on hold when the conference has a single caller. Optionally,
149 specify a musiconhold class to use. If one is not provided, it will use the
150 channel's currently set music class, or <literal>default</literal>.</para>
151 <argument name="class" required="true" />
154 <para>Disable the denoiser. By default, if <literal>func_speex</literal> is loaded, Asterisk
155 will apply a denoiser to channels in the MeetMe conference. However, channel
156 drivers that present audio with a varying rate will experience degraded
157 performance with a denoiser attached. This parameter allows a channel joining
158 the conference to choose not to have a denoiser attached without having to
159 unload <literal>func_speex</literal>.</para>
162 <para>Set talker optimization - treats talkers who aren't speaking as
163 being muted, meaning (a) No encode is done on transmission and (b)
164 Received audio that is not registered as talking is omitted causing no
165 buildup in background noise.</para>
167 <option name="p" hasparams="optional">
168 <para>Allow user to exit the conference by pressing <literal>#</literal> (default)
169 or any of the defined keys. Dial plan execution will continue at the next
170 priority following MeetMe. The key used is set to channel variable
171 <variable>MEETME_EXIT_KEY</variable>.</para>
172 <argument name="keys" required="true" />
174 <para>Option <literal>s</literal> has priority for <literal>*</literal>
175 since it cannot change its activation code.</para>
179 <para>Always prompt for the pin even if it is specified.</para>
182 <para>Quiet mode (don't play enter/leave sounds).</para>
185 <para>Record conference (records as <variable>MEETME_RECORDINGFILE</variable>
186 using format <variable>MEETME_RECORDINGFORMAT</variable>. Default filename is
187 <literal>meetme-conf-rec-${CONFNO}-${UNIQUEID}</literal> and the default format is
191 <para>Present menu (user or admin) when <literal>*</literal> is received
192 (send to menu).</para>
195 <para>Set talk only mode. (Talk only, no listening).</para>
198 <para>Set talker detection (sent to manager interface and meetme list).</para>
200 <option name="v" hasparams="optional">
201 <para>Announce when a user is joining or leaving the conference. Use the voicemail greeting as the announcement.
202 If the i or I options are set, the application will fall back to them if no voicemail greeting can be found.</para>
203 <argument name="mailbox@[context]" required="true">
204 <para>The mailbox and voicemail context to play from. If no context provided, assumed context is default.</para>
207 <option name="w" hasparams="optional">
208 <para>Wait until the marked user enters the conference.</para>
209 <argument name="secs" required="true" />
212 <para>Leave the conference when the last marked user leaves.</para>
215 <para>Allow user to exit the conference by entering a valid single digit
216 extension <variable>MEETME_EXIT_CONTEXT</variable> or the current context
217 if that variable is not defined.</para>
219 <para>Option <literal>s</literal> has priority for <literal>*</literal>
220 since it cannot change its activation code.</para>
224 <para>Do not play message when first person enters</para>
227 <para>Kick the user <replaceable>x</replaceable> seconds <emphasis>after</emphasis> he entered into
228 the conference.</para>
229 <argument name="x" required="true" />
231 <option name="L" argsep=":">
232 <para>Limit the conference to <replaceable>x</replaceable> ms. Play a warning when
233 <replaceable>y</replaceable> ms are left. Repeat the warning every <replaceable>z</replaceable> ms.
234 The following special variables can be used with this option:</para>
236 <variable name="CONF_LIMIT_TIMEOUT_FILE">
237 <para>File to play when time is up.</para>
239 <variable name="CONF_LIMIT_WARNING_FILE">
240 <para>File to play as warning if <replaceable>y</replaceable> is defined. The
241 default is to say the time remaining.</para>
244 <argument name="x" />
245 <argument name="y" />
246 <argument name="z" />
250 <parameter name="pin" />
253 <para>Enters the user into a specified MeetMe conference. If the <replaceable>confno</replaceable>
254 is omitted, the user will be prompted to enter one. User can exit the conference by hangup, or
255 if the <literal>p</literal> option is specified, by pressing <literal>#</literal>.</para>
256 <note><para>The DAHDI kernel modules and a functional DAHDI timing source (see dahdi_test)
257 must be present for conferencing to operate properly. In addition, the chan_dahdi channel driver
258 must be loaded for the <literal>i</literal> and <literal>r</literal> options to operate at
262 <ref type="application">MeetMeCount</ref>
263 <ref type="application">MeetMeAdmin</ref>
264 <ref type="application">MeetMeChannelAdmin</ref>
267 <application name="MeetMeCount" language="en_US">
269 MeetMe participant count.
272 <parameter name="confno" required="true">
273 <para>Conference number.</para>
275 <parameter name="var" />
278 <para>Plays back the number of users in the specified MeetMe conference.
279 If <replaceable>var</replaceable> is specified, playback will be skipped and the value
280 will be returned in the variable. Upon application completion, MeetMeCount will hangup
281 the channel, unless priority <literal>n+1</literal> exists, in which case priority progress will
285 <ref type="application">MeetMe</ref>
288 <application name="MeetMeAdmin" language="en_US">
290 MeetMe conference administration.
293 <parameter name="confno" required="true" />
294 <parameter name="command" required="true">
297 <para>Eject last user that joined.</para>
300 <para>Extend conference end time, if scheduled.</para>
303 <para>Kick one user out of conference.</para>
306 <para>Kick all users out of conference.</para>
309 <para>Unlock conference.</para>
312 <para>Lock conference.</para>
315 <para>Unmute one user.</para>
318 <para>Mute one user.</para>
321 <para>Unmute all users in the conference.</para>
324 <para>Mute all non-admin users in the conference.</para>
327 <para>Reset one user's volume settings.</para>
330 <para>Reset all users volume settings.</para>
333 <para>Lower entire conference speaking volume.</para>
336 <para>Raise entire conference speaking volume.</para>
339 <para>Lower one user's talk volume.</para>
342 <para>Raise one user's talk volume.</para>
345 <para>Lower one user's listen volume.</para>
348 <para>Raise one user's listen volume.</para>
351 <para>Lower entire conference listening volume.</para>
354 <para>Raise entire conference listening volume.</para>
358 <parameter name="user" />
361 <para>Run admin <replaceable>command</replaceable> for conference <replaceable>confno</replaceable>.</para>
362 <para>Will additionally set the variable <variable>MEETMEADMINSTATUS</variable> with one of
363 the following values:</para>
365 <variable name="MEETMEADMINSTATUS">
366 <value name="NOPARSE">
369 <value name="NOTFOUND">
370 User specified was not found.
372 <value name="FAILED">
373 Another failure occurred.
376 The operation was completed successfully.
382 <ref type="application">MeetMe</ref>
385 <application name="MeetMeChannelAdmin" language="en_US">
387 MeetMe conference Administration (channel specific).
390 <parameter name="channel" required="true" />
391 <parameter name="command" required="true">
394 <para>Kick the specified user out of the conference he is in.</para>
397 <para>Unmute the specified user.</para>
400 <para>Mute the specified user.</para>
406 <para>Run admin <replaceable>command</replaceable> for a specific
407 <replaceable>channel</replaceable> in any conference.</para>
410 <application name="SLAStation" language="en_US">
412 Shared Line Appearance Station.
415 <parameter name="station" required="true">
416 <para>Station name</para>
420 <para>This application should be executed by an SLA station. The argument depends
421 on how the call was initiated. If the phone was just taken off hook, then the argument
422 <replaceable>station</replaceable> should be just the station name. If the call was
423 initiated by pressing a line key, then the station name should be preceded by an underscore
424 and the trunk name associated with that line button.</para>
425 <para>For example: <literal>station1_line1</literal></para>
426 <para>On exit, this application will set the variable <variable>SLASTATION_STATUS</variable> to
427 one of the following values:</para>
429 <variable name="SLASTATION_STATUS">
430 <value name="FAILURE" />
431 <value name="CONGESTION" />
432 <value name="SUCCESS" />
437 <application name="SLATrunk" language="en_US">
439 Shared Line Appearance Trunk.
442 <parameter name="trunk" required="true">
443 <para>Trunk name</para>
445 <parameter name="options">
447 <option name="M" hasparams="optional">
448 <para>Play back the specified MOH <replaceable>class</replaceable>
449 instead of ringing</para>
450 <argument name="class" required="true" />
456 <para>This application should be executed by an SLA trunk on an inbound call. The channel calling
457 this application should correspond to the SLA trunk with the name <replaceable>trunk</replaceable>
458 that is being passed as an argument.</para>
459 <para>On exit, this application will set the variable <variable>SLATRUNK_STATUS</variable> to
460 one of the following values:</para>
462 <variable name="SLATRUNK_STATUS">
463 <value name="FAILURE" />
464 <value name="SUCCESS" />
465 <value name="UNANSWERED" />
466 <value name="RINGTIMEOUT" />
471 <function name="MEETME_INFO" language="en_US">
473 Query a given conference of various properties.
476 <parameter name="keyword" required="true">
477 <para>Options:</para>
480 <para>Boolean of whether the corresponding conference is locked.</para>
482 <enum name="parties">
483 <para>Number of parties in a given conference</para>
485 <enum name="activity">
486 <para>Duration of conference in seconds.</para>
488 <enum name="dynamic">
489 <para>Boolean of whether the corresponding conference is dynamic.</para>
493 <parameter name="confno" required="true">
494 <para>Conference number to retrieve information from.</para>
499 <ref type="application">MeetMe</ref>
500 <ref type="application">MeetMeCount</ref>
501 <ref type="application">MeetMeAdmin</ref>
502 <ref type="application">MeetMeChannelAdmin</ref>
505 <manager name="MeetmeMute" language="en_US">
510 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
511 <parameter name="Meetme" required="true" />
512 <parameter name="Usernum" required="true" />
517 <manager name="MeetmeUnmute" language="en_US">
519 Unmute a Meetme user.
522 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
523 <parameter name="Meetme" required="true" />
524 <parameter name="Usernum" required="true" />
529 <manager name="MeetmeList" language="en_US">
531 List participants in a conference.
534 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
535 <parameter name="Conference" required="false">
536 <para>Conference number.</para>
540 <para>Lists all users in a particular MeetMe conference.
541 MeetmeList will follow as separate events, followed by a final event called
542 MeetmeListComplete.</para>
545 <manager name="MeetmeListRooms" language="en_US">
547 List active conferences.
550 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
553 <para>Lists data about all active conferences.
554 MeetmeListRooms will follow as separate events, followed by a final event called
555 MeetmeListRoomsComplete.</para>
558 <managerEvent language="en_US" name="MeetmeJoin">
559 <managerEventInstance class="EVENT_FLAG_CALL">
560 <synopsis>Raised when a user joins a MeetMe conference.</synopsis>
562 <parameter name="Meetme">
563 <para>The identifier for the MeetMe conference.</para>
565 <parameter name="User">
566 <para>The identifier of the MeetMe user who joined.</para>
571 <ref type="managerEvent">MeetmeLeave</ref>
572 <ref type="application">MeetMe</ref>
574 </managerEventInstance>
576 <managerEvent language="en_US" name="MeetmeLeave">
577 <managerEventInstance class="EVENT_FLAG_CALL">
578 <synopsis>Raised when a user leaves a MeetMe conference.</synopsis>
580 <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter)" />
582 <parameter name="Duration">
583 <para>The length of time in seconds that the Meetme user was in the conference.</para>
587 <ref type="managerEvent">MeetmeJoin</ref>
589 </managerEventInstance>
591 <managerEvent language="en_US" name="MeetmeEnd">
592 <managerEventInstance class="EVENT_FLAG_CALL">
593 <synopsis>Raised when a MeetMe conference ends.</synopsis>
595 <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
598 <ref type="managerEvent">MeetmeJoin</ref>
600 </managerEventInstance>
602 <managerEvent language="en_US" name="MeetmeTalkRequest">
603 <managerEventInstance class="EVENT_FLAG_CALL">
604 <synopsis>Raised when a MeetMe user has started talking.</synopsis>
606 <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter)" />
608 <parameter name="Duration">
609 <para>The length of time in seconds that the Meetme user has been in the conference at the time of this event.</para>
611 <parameter name="Status">
618 </managerEventInstance>
620 <managerEvent language="en_US" name="MeetmeTalking">
621 <managerEventInstance class="EVENT_FLAG_CALL">
622 <synopsis>Raised when a MeetMe user begins or ends talking.</synopsis>
624 <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter)" />
626 <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeTalkRequest']/managerEventInstance/syntax/parameter)" />
628 </managerEventInstance>
630 <managerEvent language="en_US" name="MeetmeMute">
631 <managerEventInstance class="EVENT_FLAG_CALL">
632 <synopsis>Raised when a MeetMe user is muted or unmuted.</synopsis>
634 <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter)" />
636 <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeTalkRequest']/managerEventInstance/syntax/parameter)" />
638 </managerEventInstance>
642 #define CONFIG_FILE_NAME "meetme.conf"
643 #define SLA_CONFIG_FILE "sla.conf"
644 #define STR_CONCISE "concise"
646 /*! each buffer is 20ms, so this is 640ms total */
647 #define DEFAULT_AUDIO_BUFFERS 32
649 /*! String format for scheduled conferences */
650 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
653 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
654 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
655 ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */
656 /*! User has requested to speak */
657 ADMINFLAG_T_REQUEST = (1 << 4),
658 ADMINFLAG_HANGUP = (1 << 5), /*!< User will be leaving the conference */
661 #define MEETME_DELAYDETECTTALK 300
662 #define MEETME_DELAYDETECTENDTALK 1000
664 #define AST_FRAME_BITS 32
671 enum entrance_sound {
676 enum recording_state {
678 MEETME_RECORD_STARTED,
679 MEETME_RECORD_ACTIVE,
680 MEETME_RECORD_TERMINATE
683 #define CONF_SIZE 320
686 /*! user has admin access on the conference */
687 CONFFLAG_ADMIN = (1 << 0),
688 /*! If set the user can only receive audio from the conference */
689 CONFFLAG_MONITOR = (1 << 1),
690 /*! If set asterisk will exit conference when key defined in p() option is pressed */
691 CONFFLAG_KEYEXIT = (1 << 2),
692 /*! If set asterisk will provide a menu to the user when '*' is pressed */
693 CONFFLAG_STARMENU = (1 << 3),
694 /*! If set the use can only send audio to the conference */
695 CONFFLAG_TALKER = (1 << 4),
696 /*! If set there will be no enter or leave sounds */
697 CONFFLAG_QUIET = (1 << 5),
698 /*! If set, when user joins the conference, they will be told the number
699 * of users that are already in */
700 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
701 /*! Set to run AGI Script in Background */
702 CONFFLAG_AGI = (1 << 7),
703 /*! Set to have music on hold when user is alone in conference */
704 CONFFLAG_MOH = (1 << 8),
705 /*! If set, the channel will leave the conference if all marked users leave */
706 CONFFLAG_MARKEDEXIT = (1 << 9),
707 /*! If set, the MeetMe will wait until a marked user enters */
708 CONFFLAG_WAITMARKED = (1 << 10),
709 /*! If set, the MeetMe will exit to the specified context */
710 CONFFLAG_EXIT_CONTEXT = (1 << 11),
711 /*! If set, the user will be marked */
712 CONFFLAG_MARKEDUSER = (1 << 12),
713 /*! If set, user will be ask record name on entry of conference */
714 CONFFLAG_INTROUSER = (1 << 13),
715 /*! If set, the MeetMe will be recorded */
716 CONFFLAG_RECORDCONF = (1<< 14),
717 /*! If set, the user will be monitored if the user is talking or not */
718 CONFFLAG_MONITORTALKER = (1 << 15),
719 CONFFLAG_DYNAMIC = (1 << 16),
720 CONFFLAG_DYNAMICPIN = (1 << 17),
721 CONFFLAG_EMPTY = (1 << 18),
722 CONFFLAG_EMPTYNOPIN = (1 << 19),
723 CONFFLAG_ALWAYSPROMPT = (1 << 20),
724 /*! If set, treat talking users as muted users */
725 CONFFLAG_OPTIMIZETALKER = (1 << 21),
726 /*! If set, won't speak the extra prompt when the first person
727 * enters the conference */
728 CONFFLAG_NOONLYPERSON = (1 << 22),
729 /*! If set, user will be asked to record name on entry of conference
731 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
732 /*! If set, the user will be initially self-muted */
733 CONFFLAG_STARTMUTED = (1 << 24),
734 /*! Pass DTMF through the conference */
735 CONFFLAG_PASS_DTMF = (1 << 25),
736 CONFFLAG_SLA_STATION = (1 << 26),
737 CONFFLAG_SLA_TRUNK = (1 << 27),
738 /*! If set, the user should continue in the dialplan if kicked out */
739 CONFFLAG_KICK_CONTINUE = (1 << 28),
740 CONFFLAG_DURATION_STOP = (1 << 29),
741 CONFFLAG_DURATION_LIMIT = (1 << 30),
744 /* These flags are defined separately because we ran out of bits that an enum can be used to represent.
745 If you add new flags, be sure to do it in the same way that these are. */
746 /*! Do not write any audio to this channel until the state is up. */
747 #define CONFFLAG_NO_AUDIO_UNTIL_UP (1ULL << 31)
748 #define CONFFLAG_INTROMSG (1ULL << 32) /*!< If set play an intro announcement at start of conference */
749 #define CONFFLAG_INTROUSER_VMREC (1ULL << 33)
750 /*! If there's only one person left in a conference when someone leaves, kill the conference */
751 #define CONFFLAG_KILL_LAST_MAN_STANDING (1ULL << 34)
752 /*! If set, don't enable a denoiser for the channel */
753 #define CONFFLAG_DONT_DENOISE (1ULL << 35)
756 OPT_ARG_WAITMARKED = 0,
757 OPT_ARG_EXITKEYS = 1,
758 OPT_ARG_DURATION_STOP = 2,
759 OPT_ARG_DURATION_LIMIT = 3,
760 OPT_ARG_MOH_CLASS = 4,
761 OPT_ARG_INTROMSG = 5,
762 OPT_ARG_INTROUSER_VMREC = 6,
763 OPT_ARG_ARRAY_SIZE = 7,
766 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
767 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
768 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
769 AST_APP_OPTION('b', CONFFLAG_AGI ),
770 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
771 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
772 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
773 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
774 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
775 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
776 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
777 AST_APP_OPTION_ARG('G', CONFFLAG_INTROMSG, OPT_ARG_INTROMSG ),
778 AST_APP_OPTION_ARG('v', CONFFLAG_INTROUSER_VMREC , OPT_ARG_INTROUSER_VMREC),
779 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
780 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
781 AST_APP_OPTION('k', CONFFLAG_KILL_LAST_MAN_STANDING ),
782 AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
783 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
784 AST_APP_OPTION('n', CONFFLAG_DONT_DENOISE ),
785 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
786 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
787 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
788 AST_APP_OPTION('q', CONFFLAG_QUIET ),
789 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
790 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
791 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
792 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
793 AST_APP_OPTION('t', CONFFLAG_TALKER ),
794 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
795 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
796 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
797 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
798 AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
799 AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
802 static const char * const app = "MeetMe";
803 static const char * const app2 = "MeetMeCount";
804 static const char * const app3 = "MeetMeAdmin";
805 static const char * const app4 = "MeetMeChannelAdmin";
806 static const char * const slastation_app = "SLAStation";
807 static const char * const slatrunk_app = "SLATrunk";
809 /* Lookup RealTime conferences based on confno and current time */
810 static int rt_schedule;
811 static int fuzzystart;
812 static int earlyalert;
816 /*! Log participant count to the RealTime backend */
817 static int rt_log_members;
819 #define MAX_CONFNUM 80
821 #define OPTIONS_LEN 100
823 /* Enough space for "<conference #>,<pin>,<admin pin>" followed by a 0 byte. */
824 #define MAX_SETTINGS (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)
831 struct announce_listitem {
832 AST_LIST_ENTRY(announce_listitem) entry;
833 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
834 char language[MAX_LANGUAGE];
835 struct ast_channel *confchan;
838 enum announcetypes announcetype;
841 /*! \brief The MeetMe Conference object */
842 struct ast_conference {
843 ast_mutex_t playlock; /*!< Conference specific lock (players) */
844 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
845 char confno[MAX_CONFNUM]; /*!< Conference */
846 struct ast_channel *chan; /*!< Announcements channel */
847 struct ast_channel *lchan; /*!< Listen/Record channel */
848 int fd; /*!< Announcements fd */
849 int dahdiconf; /*!< DAHDI Conf # */
850 int users; /*!< Number of active users */
851 int markedusers; /*!< Number of marked users */
852 int maxusers; /*!< Participant limit if scheduled */
853 int endalert; /*!< When to play conf ending message */
854 time_t start; /*!< Start time (s) */
855 int refcount; /*!< reference count of usage */
856 enum recording_state recording:2; /*!< recording status */
857 unsigned int isdynamic:1; /*!< Created on the fly? */
858 unsigned int locked:1; /*!< Is the conference locked? */
859 unsigned int gmuted:1; /*!< Is the conference globally muted? (all non-admins) */
860 pthread_t recordthread; /*!< thread for recording */
861 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
862 pthread_attr_t attr; /*!< thread attribute */
863 char *recordingfilename; /*!< Filename to record the Conference into */
864 char *recordingformat; /*!< Format to record the Conference in */
865 char pin[MAX_PIN]; /*!< If protected by a PIN */
866 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
868 long endtime; /*!< When to end the conf if scheduled */
869 const char *useropts; /*!< RealTime user flags */
870 const char *adminopts; /*!< RealTime moderator flags */
871 const char *bookid; /*!< RealTime conference id */
872 struct ast_frame *transframe[32];
873 struct ast_frame *origframe;
874 struct ast_trans_pvt *transpath[32];
875 struct ao2_container *usercontainer;
876 AST_LIST_ENTRY(ast_conference) list;
877 /* announce_thread related data */
878 pthread_t announcethread;
879 ast_mutex_t announcethreadlock;
880 unsigned int announcethread_stop:1;
881 ast_cond_t announcelist_addition;
882 AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
883 ast_mutex_t announcelistlock;
886 static AST_LIST_HEAD_STATIC(confs, ast_conference);
888 static unsigned int conf_map[1024] = {0, };
891 int desired; /*!< Desired volume adjustment */
892 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
895 /*! \brief The MeetMe User object */
896 struct ast_conf_user {
897 int user_no; /*!< User Number */
898 struct ast_flags64 userflags; /*!< Flags as set in the conference */
899 int adminflags; /*!< Flags set by the Admin */
900 struct ast_channel *chan; /*!< Connected channel */
901 int talking; /*!< Is user talking */
902 int dahdichannel; /*!< Is a DAHDI channel */
903 char usrvalue[50]; /*!< Custom User Value */
904 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
905 time_t jointime; /*!< Time the user joined the conference */
906 time_t kicktime; /*!< Time the user will be kicked from the conference */
907 struct timeval start_time; /*!< Time the user entered into the conference */
908 long timelimit; /*!< Time limit for the user to be in the conference L(x:y:z) */
909 long play_warning; /*!< Play a warning when 'y' ms are left */
910 long warning_freq; /*!< Repeat the warning every 'z' ms */
911 const char *warning_sound; /*!< File to play as warning if 'y' is defined */
912 const char *end_sound; /*!< File to play when time is up. */
914 struct volume listen;
915 AST_LIST_ENTRY(ast_conf_user) list;
918 enum sla_which_trunk_refs {
923 enum sla_trunk_state {
924 SLA_TRUNK_STATE_IDLE,
925 SLA_TRUNK_STATE_RINGING,
927 SLA_TRUNK_STATE_ONHOLD,
928 SLA_TRUNK_STATE_ONHOLD_BYME,
931 enum sla_hold_access {
932 /*! This means that any station can put it on hold, and any station
933 * can retrieve the call from hold. */
935 /*! This means that only the station that put the call on hold may
936 * retrieve it from hold. */
940 struct sla_trunk_ref;
943 AST_RWLIST_ENTRY(sla_station) entry;
944 AST_DECLARE_STRING_FIELDS(
945 AST_STRING_FIELD(name);
946 AST_STRING_FIELD(device);
947 AST_STRING_FIELD(autocontext);
949 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
950 struct ast_dial *dial;
951 /*! Ring timeout for this station, for any trunk. If a ring timeout
952 * is set for a specific trunk on this station, that will take
953 * priority over this value. */
954 unsigned int ring_timeout;
955 /*! Ring delay for this station, for any trunk. If a ring delay
956 * is set for a specific trunk on this station, that will take
957 * priority over this value. */
958 unsigned int ring_delay;
959 /*! This option uses the values in the sla_hold_access enum and sets the
960 * access control type for hold on this station. */
961 unsigned int hold_access:1;
962 /*! Mark used during reload processing */
967 * \brief A reference to a station
969 * This struct looks near useless at first glance. However, its existence
970 * in the list of stations in sla_trunk means that this station references
971 * that trunk. We use the mark to keep track of whether it needs to be
972 * removed from the sla_trunk's list of stations during a reload.
974 struct sla_station_ref {
975 AST_LIST_ENTRY(sla_station_ref) entry;
976 struct sla_station *station;
977 /*! Mark used during reload processing */
982 AST_DECLARE_STRING_FIELDS(
983 AST_STRING_FIELD(name);
984 AST_STRING_FIELD(device);
985 AST_STRING_FIELD(autocontext);
987 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
988 /*! Number of stations that use this trunk */
989 unsigned int num_stations;
990 /*! Number of stations currently on a call with this trunk */
991 unsigned int active_stations;
992 /*! Number of stations that have this trunk on hold. */
993 unsigned int hold_stations;
994 struct ast_channel *chan;
995 unsigned int ring_timeout;
996 /*! If set to 1, no station will be able to join an active call with
998 unsigned int barge_disabled:1;
999 /*! This option uses the values in the sla_hold_access enum and sets the
1000 * access control type for hold on this trunk. */
1001 unsigned int hold_access:1;
1002 /*! Whether this trunk is currently on hold, meaning that once a station
1003 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
1004 unsigned int on_hold:1;
1005 /*! Mark used during reload processing */
1006 unsigned int mark:1;
1010 * \brief A station's reference to a trunk
1012 * An sla_station keeps a list of trunk_refs. This holds metadata about the
1013 * stations usage of the trunk.
1015 struct sla_trunk_ref {
1016 AST_LIST_ENTRY(sla_trunk_ref) entry;
1017 struct sla_trunk *trunk;
1018 enum sla_trunk_state state;
1019 struct ast_channel *chan;
1020 /*! Ring timeout to use when this trunk is ringing on this specific
1021 * station. This takes higher priority than a ring timeout set at
1022 * the station level. */
1023 unsigned int ring_timeout;
1024 /*! Ring delay to use when this trunk is ringing on this specific
1025 * station. This takes higher priority than a ring delay set at
1026 * the station level. */
1027 unsigned int ring_delay;
1028 /*! Mark used during reload processing */
1029 unsigned int mark:1;
1032 static struct ao2_container *sla_stations;
1033 static struct ao2_container *sla_trunks;
1035 static const char sla_registrar[] = "SLA";
1037 /*! \brief Event types that can be queued up for the SLA thread */
1038 enum sla_event_type {
1039 /*! A station has put the call on hold */
1041 /*! The state of a dial has changed */
1042 SLA_EVENT_DIAL_STATE,
1043 /*! The state of a ringing trunk has changed */
1044 SLA_EVENT_RINGING_TRUNK,
1048 enum sla_event_type type;
1049 struct sla_station *station;
1050 struct sla_trunk_ref *trunk_ref;
1051 AST_LIST_ENTRY(sla_event) entry;
1054 /*! \brief A station that failed to be dialed
1055 * \note Only used by the SLA thread. */
1056 struct sla_failed_station {
1057 struct sla_station *station;
1058 struct timeval last_try;
1059 AST_LIST_ENTRY(sla_failed_station) entry;
1062 /*! \brief A trunk that is ringing */
1063 struct sla_ringing_trunk {
1064 struct sla_trunk *trunk;
1065 /*! The time that this trunk started ringing */
1066 struct timeval ring_begin;
1067 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
1068 AST_LIST_ENTRY(sla_ringing_trunk) entry;
1071 enum sla_station_hangup {
1072 SLA_STATION_HANGUP_NORMAL,
1073 SLA_STATION_HANGUP_TIMEOUT,
1076 /*! \brief A station that is ringing */
1077 struct sla_ringing_station {
1078 struct sla_station *station;
1079 /*! The time that this station started ringing */
1080 struct timeval ring_begin;
1081 AST_LIST_ENTRY(sla_ringing_station) entry;
1085 * \brief A structure for data used by the sla thread
1088 /*! The SLA thread ID */
1092 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
1093 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
1094 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
1095 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
1096 unsigned int stop:1;
1097 /*! Attempt to handle CallerID, even though it is known not to work
1098 * properly in some situations. */
1099 unsigned int attempt_callerid:1;
1101 .thread = AST_PTHREADT_NULL,
1104 /*! \brief The number of audio buffers to be allocated on pseudo channels
1105 * when in a conference */
1106 static int audio_buffers;
1108 /*! \brief Map 'volume' levels from -5 through +5 into decibel (dB)
1109 * settings for channel drivers.
1111 * \note these are not a straight linear-to-dB
1112 * conversion... the numbers have been modified
1113 * to give the user a better level of adjustability.
1115 static const char gain_map[] = {
1129 /* Routes the various meetme message types to the meetme stasis callback function to turn them into events */
1130 static struct stasis_message_router *meetme_event_message_router;
1132 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_join_type);
1133 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_leave_type);
1134 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_end_type);
1135 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_mute_type);
1136 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_talking_type);
1137 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_talk_request_type);
1139 static void meetme_stasis_cb(void *data, struct stasis_subscription *sub,
1140 struct stasis_message *message);
1142 static void meetme_stasis_cleanup(void)
1144 if (meetme_event_message_router) {
1145 stasis_message_router_unsubscribe(meetme_event_message_router);
1146 meetme_event_message_router = NULL;
1149 STASIS_MESSAGE_TYPE_CLEANUP(meetme_join_type);
1150 STASIS_MESSAGE_TYPE_CLEANUP(meetme_leave_type);
1151 STASIS_MESSAGE_TYPE_CLEANUP(meetme_end_type);
1152 STASIS_MESSAGE_TYPE_CLEANUP(meetme_mute_type);
1153 STASIS_MESSAGE_TYPE_CLEANUP(meetme_talking_type);
1154 STASIS_MESSAGE_TYPE_CLEANUP(meetme_talk_request_type);
1157 static int meetme_stasis_init(void)
1160 STASIS_MESSAGE_TYPE_INIT(meetme_join_type);
1161 STASIS_MESSAGE_TYPE_INIT(meetme_leave_type);
1162 STASIS_MESSAGE_TYPE_INIT(meetme_end_type);
1163 STASIS_MESSAGE_TYPE_INIT(meetme_mute_type);
1164 STASIS_MESSAGE_TYPE_INIT(meetme_talking_type);
1165 STASIS_MESSAGE_TYPE_INIT(meetme_talk_request_type);
1167 meetme_event_message_router = stasis_message_router_create(
1168 ast_channel_topic_all_cached());
1170 if (!meetme_event_message_router) {
1171 meetme_stasis_cleanup();
1175 if (stasis_message_router_add(meetme_event_message_router,
1179 meetme_stasis_cleanup();
1183 if (stasis_message_router_add(meetme_event_message_router,
1184 meetme_leave_type(),
1187 meetme_stasis_cleanup();
1191 if (stasis_message_router_add(meetme_event_message_router,
1195 meetme_stasis_cleanup();
1199 if (stasis_message_router_add(meetme_event_message_router,
1203 meetme_stasis_cleanup();
1207 if (stasis_message_router_add(meetme_event_message_router,
1208 meetme_talking_type(),
1211 meetme_stasis_cleanup();
1215 if (stasis_message_router_add(meetme_event_message_router,
1216 meetme_talk_request_type(),
1219 meetme_stasis_cleanup();
1226 static void meetme_stasis_cb(void *data, struct stasis_subscription *sub,
1227 struct stasis_message *message)
1229 struct ast_channel_blob *channel_blob = stasis_message_data(message);
1230 struct stasis_message_type *message_type;
1232 const char *conference_num;
1234 struct ast_json *json_cur;
1235 RAII_VAR(struct ast_str *, channel_text, NULL, ast_free);
1236 RAII_VAR(struct ast_str *, extra_text, NULL, ast_free);
1238 if (!channel_blob) {
1243 message_type = stasis_message_type(message);
1245 if (!message_type) {
1250 if (message_type == meetme_join_type()) {
1251 event = "MeetmeJoin";
1252 } else if (message_type == meetme_leave_type()) {
1253 event = "MeetmeLeave";
1254 } else if (message_type == meetme_end_type()) {
1255 event = "MeetmeEnd";
1256 } else if (message_type == meetme_mute_type()) {
1257 event = "MeetmeMute";
1258 } else if (message_type == meetme_talking_type()) {
1259 event = "MeetmeTalking";
1260 } else if (message_type == meetme_talk_request_type()) {
1261 event = "MeetmeTalkRequest";
1272 conference_num = ast_json_string_get(ast_json_object_get(channel_blob->blob, "Meetme"));
1273 if (!conference_num) {
1278 status = ast_json_string_get(ast_json_object_get(channel_blob->blob, "status"));
1280 ast_str_append_event_header(&extra_text, "Status", status);
1283 if (channel_blob->snapshot) {
1284 channel_text = ast_manager_build_channel_state_string(channel_blob->snapshot);
1287 if ((json_cur = ast_json_object_get(channel_blob->blob, "user"))) {
1288 int user_number = ast_json_integer_get(json_cur);
1289 RAII_VAR(struct ast_str *, user_prop_str, ast_str_create(32), ast_free);
1290 if (!user_prop_str) {
1294 ast_str_set(&user_prop_str, 0, "%d", user_number);
1295 ast_str_append_event_header(&extra_text, "User", ast_str_buffer(user_prop_str));
1297 if ((json_cur = ast_json_object_get(channel_blob->blob, "duration"))) {
1298 int duration = ast_json_integer_get(json_cur);
1299 ast_str_set(&user_prop_str, 0, "%d", duration);
1300 ast_str_append_event_header(&extra_text, "Duration", ast_str_buffer(user_prop_str));
1306 manager_event(EVENT_FLAG_CALL, event,
1311 channel_text ? ast_str_buffer(channel_text) : "",
1312 extra_text ? ast_str_buffer(extra_text) : "");
1317 * \brief Build a json object from a status value for inclusion in json extras for meetme_stasis_generate_msg
1320 * \param on if true, then status is on. Otherwise status is off
1321 * \retval NULL on failure to allocate the JSON blob.
1322 * \retval pointer to the JSON blob if successful.
1324 static struct ast_json *status_to_json(int on)
1326 struct ast_json *json_object = ast_json_pack("{s: s}",
1327 "status", on ? "on" : "off");
1334 * \brief Generate a stasis message associated with a meetme event
1337 * \param meetme_confere The conference responsible for generating this message
1338 * \param chan The channel involved in the message (NULL allowed)
1339 * \param user The conference user involved in the message (NULL allowed)
1340 * \param message_type the type the stasis message being generated
1341 * \param extras Additional json fields desired for inclusion
1343 static void meetme_stasis_generate_msg(struct ast_conference *meetme_conference, struct ast_channel *chan,
1344 struct ast_conf_user *user, struct stasis_message_type *message_type, struct ast_json *extras)
1346 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
1347 RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
1349 json_object = ast_json_pack("{s: s}",
1350 "Meetme", meetme_conference->confno);
1357 ast_json_object_update(json_object, extras);
1361 struct timeval now = ast_tvnow();
1362 long duration = (long)(now.tv_sec - user->jointime);
1363 struct ast_json *json_user;
1364 struct ast_json *json_user_duration;
1366 json_user = ast_json_integer_create(user->user_no);
1367 if (!json_user || ast_json_object_set(json_object, "user", json_user)) {
1372 json_user_duration = ast_json_integer_create(duration);
1373 if (!json_user_duration
1374 || ast_json_object_set(json_object, "duration", json_user_duration)) {
1381 ast_channel_lock(chan);
1383 msg = ast_channel_blob_create(chan, message_type, json_object);
1385 ast_channel_unlock(chan);
1392 stasis_publish(ast_channel_topic(chan), msg);
1395 static int admin_exec(struct ast_channel *chan, const char *data);
1396 static void *recordthread(void *args);
1398 static const char *istalking(int x)
1403 return "(unmonitored)";
1405 return "(not talking)";
1408 static int careful_write(int fd, unsigned char *data, int len, int block)
1415 x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
1416 res = ioctl(fd, DAHDI_IOMUX, &x);
1420 res = write(fd, data, len);
1422 if (errno != EAGAIN) {
1423 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
1435 static int set_talk_volume(struct ast_conf_user *user, int volume)
1439 /* attempt to make the adjustment in the channel driver;
1440 if successful, don't adjust in the frame reading routine
1442 gain_adjust = gain_map[volume + 5];
1444 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1447 static int set_listen_volume(struct ast_conf_user *user, int volume)
1451 /* attempt to make the adjustment in the channel driver;
1452 if successful, don't adjust in the frame reading routine
1454 gain_adjust = gain_map[volume + 5];
1456 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1459 static void tweak_volume(struct volume *vol, enum volume_action action)
1463 switch (vol->desired) {
1478 switch (vol->desired) {
1494 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
1496 tweak_volume(&user->talk, action);
1497 /* attempt to make the adjustment in the channel driver;
1498 if successful, don't adjust in the frame reading routine
1500 if (!set_talk_volume(user, user->talk.desired))
1501 user->talk.actual = 0;
1503 user->talk.actual = user->talk.desired;
1506 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
1508 tweak_volume(&user->listen, action);
1509 /* attempt to make the adjustment in the channel driver;
1510 if successful, don't adjust in the frame reading routine
1512 if (!set_listen_volume(user, user->listen.desired))
1513 user->listen.actual = 0;
1515 user->listen.actual = user->listen.desired;
1518 static void reset_volumes(struct ast_conf_user *user)
1520 signed char zero_volume = 0;
1522 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
1523 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
1526 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
1528 unsigned char *data;
1532 ast_test_suite_event_notify("CONFPLAY", "Channel: %s\r\n"
1533 "Conference: %s\r\n"
1535 ast_channel_name(chan),
1539 if (!ast_check_hangup(chan))
1540 res = ast_autoservice_start(chan);
1542 AST_LIST_LOCK(&confs);
1547 len = sizeof(enter);
1551 len = sizeof(leave);
1558 careful_write(conf->fd, data, len, 1);
1561 AST_LIST_UNLOCK(&confs);
1564 ast_autoservice_stop(chan);
1567 static int user_no_cmp(void *obj, void *arg, int flags)
1569 struct ast_conf_user *user = obj;
1572 if (user->user_no == *user_no) {
1573 return (CMP_MATCH | CMP_STOP);
1579 static int user_max_cmp(void *obj, void *arg, int flags)
1581 struct ast_conf_user *user = obj;
1584 if (user->user_no > *max_no) {
1585 *max_no = user->user_no;
1592 * \brief Find or create a conference
1594 * \param confno The conference name/number
1595 * \param pin The regular user pin
1596 * \param pinadmin The admin pin
1597 * \param make Make the conf if it doesn't exist
1598 * \param dynamic Mark the newly created conference as dynamic
1599 * \param refcount How many references to mark on the conference
1600 * \param chan The asterisk channel
1603 * \return A pointer to the conference struct, or NULL if it wasn't found and
1604 * make or dynamic were not set.
1606 static struct ast_conference *build_conf(const char *confno, const char *pin,
1607 const char *pinadmin, int make, int dynamic, int refcount,
1608 const struct ast_channel *chan, struct ast_test *test)
1610 struct ast_conference *cnf;
1611 struct dahdi_confinfo dahdic = { 0, };
1613 struct ast_format_cap *cap_slin = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
1615 AST_LIST_LOCK(&confs);
1617 AST_LIST_TRAVERSE(&confs, cnf, list) {
1618 if (!strcmp(confno, cnf->confno))
1622 if (cnf || (!make && !dynamic) || !cap_slin)
1625 ast_format_cap_append(cap_slin, ast_format_slin, 0);
1626 /* Make a new one */
1627 if (!(cnf = ast_calloc(1, sizeof(*cnf))) ||
1628 !(cnf->usercontainer = ao2_container_alloc(1, NULL, user_no_cmp))) {
1632 ast_mutex_init(&cnf->playlock);
1633 ast_mutex_init(&cnf->listenlock);
1634 cnf->recordthread = AST_PTHREADT_NULL;
1635 ast_mutex_init(&cnf->recordthreadlock);
1636 cnf->announcethread = AST_PTHREADT_NULL;
1637 ast_mutex_init(&cnf->announcethreadlock);
1638 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
1639 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
1640 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
1641 ast_copy_string(cnf->uniqueid, ast_channel_uniqueid(chan), sizeof(cnf->uniqueid));
1643 /* Setup a new dahdi conference */
1645 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1646 cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
1647 if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
1649 /* if we are creating a conference for a unit test, it is not neccesary
1650 * to open a pseudo channel, so, if we fail continue creating
1651 * the conference. */
1652 ast_test_status_update(test, "Unable to open DAHDI pseudo device\n");
1654 ast_log(LOG_WARNING, "Unable to open DAHDI pseudo device\n");
1657 ao2_ref(cnf->usercontainer, -1);
1658 ast_mutex_destroy(&cnf->playlock);
1659 ast_mutex_destroy(&cnf->listenlock);
1660 ast_mutex_destroy(&cnf->recordthreadlock);
1661 ast_mutex_destroy(&cnf->announcethreadlock);
1668 cnf->dahdiconf = dahdic.confno;
1670 /* Setup a new channel for playback of audio files */
1671 cnf->chan = ast_request("DAHDI", cap_slin, NULL, chan, "pseudo", NULL);
1673 ast_set_read_format(cnf->chan, ast_format_slin);
1674 ast_set_write_format(cnf->chan, ast_format_slin);
1676 dahdic.confno = cnf->dahdiconf;
1677 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1678 if (ioctl(ast_channel_fd(cnf->chan, 0), DAHDI_SETCONF, &dahdic)) {
1680 ast_test_status_update(test, "Error setting conference on pseudo channel\n");
1682 ast_log(LOG_WARNING, "Error setting conference\n");
1684 ast_hangup(cnf->chan);
1687 ao2_ref(cnf->usercontainer, -1);
1688 ast_mutex_destroy(&cnf->playlock);
1689 ast_mutex_destroy(&cnf->listenlock);
1690 ast_mutex_destroy(&cnf->recordthreadlock);
1691 ast_mutex_destroy(&cnf->announcethreadlock);
1698 /* Fill the conference struct */
1699 cnf->start = time(NULL);
1700 cnf->maxusers = 0x7fffffff;
1701 cnf->isdynamic = dynamic ? 1 : 0;
1702 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
1703 AST_LIST_INSERT_HEAD(&confs, cnf, list);
1705 /* Reserve conference number in map */
1706 if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1707 conf_map[confno_int] = 1;
1710 ao2_cleanup(cap_slin);
1712 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
1714 AST_LIST_UNLOCK(&confs);
1719 static char *complete_confno(const char *word, int state)
1721 struct ast_conference *cnf;
1724 int len = strlen(word);
1726 AST_LIST_LOCK(&confs);
1727 AST_LIST_TRAVERSE(&confs, cnf, list) {
1728 if (!strncmp(word, cnf->confno, len) && ++which > state) {
1729 /* dup before releasing the lock */
1730 ret = ast_strdup(cnf->confno);
1734 AST_LIST_UNLOCK(&confs);
1738 static char *complete_userno(struct ast_conference *cnf, const char *word, int state)
1741 struct ao2_iterator iter;
1742 struct ast_conf_user *usr;
1745 int len = strlen(word);
1747 iter = ao2_iterator_init(cnf->usercontainer, 0);
1748 for (; (usr = ao2_iterator_next(&iter)); ao2_ref(usr, -1)) {
1749 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
1750 if (!strncmp(word, usrno, len) && ++which > state) {
1752 ret = ast_strdup(usrno);
1756 ao2_iterator_destroy(&iter);
1760 static char *complete_meetmecmd_mute_kick(const char *line, const char *word, int pos, int state)
1763 return complete_confno(word, state);
1766 int len = strlen(word);
1771 struct ast_conference *cnf;
1773 if (!strncasecmp(word, "all", len)) {
1775 return ast_strdup("all");
1780 /* Extract the confno from the command line. */
1781 myline = ast_strdupa(line);
1782 strtok_r(myline, " ", &saved);
1783 strtok_r(NULL, " ", &saved);
1784 confno = strtok_r(NULL, " ", &saved);
1786 AST_LIST_LOCK(&confs);
1787 AST_LIST_TRAVERSE(&confs, cnf, list) {
1788 if (!strcmp(confno, cnf->confno)) {
1789 ret = complete_userno(cnf, word, state);
1793 AST_LIST_UNLOCK(&confs);
1800 static char *complete_meetmecmd_lock(const char *word, int pos, int state)
1803 return complete_confno(word, state);
1808 static char *complete_meetmecmd_list(const char *line, const char *word, int pos, int state)
1814 if (!strncasecmp(word, STR_CONCISE, len)) {
1816 return ast_strdup(STR_CONCISE);
1821 return complete_confno(word, state);
1823 if (pos == 3 && state == 0) {
1828 /* Extract the confno from the command line. */
1829 myline = ast_strdupa(line);
1830 strtok_r(myline, " ", &saved);
1831 strtok_r(NULL, " ", &saved);
1832 confno = strtok_r(NULL, " ", &saved);
1834 if (!strcasecmp(confno, STR_CONCISE)) {
1835 /* There is nothing valid in this position now. */
1840 if (!strncasecmp(word, STR_CONCISE, len)) {
1841 return ast_strdup(STR_CONCISE);
1847 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1849 /* Process the command */
1850 struct ast_conf_user *user;
1851 struct ast_conference *cnf;
1855 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
1856 #define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
1860 e->command = "meetme list";
1862 "Usage: meetme list [<confno>] [" STR_CONCISE "]\n"
1863 " List all conferences or a specific conference.\n";
1866 return complete_meetmecmd_list(a->line, a->word, a->pos, a->n);
1869 if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], STR_CONCISE))) {
1870 /* List all the conferences */
1871 int concise = (a->argc == 3);
1872 struct ast_str *marked_users;
1874 if (!(marked_users = ast_str_create(30))) {
1879 AST_LIST_LOCK(&confs);
1880 if (AST_LIST_EMPTY(&confs)) {
1882 ast_cli(a->fd, "No active MeetMe conferences.\n");
1884 AST_LIST_UNLOCK(&confs);
1885 ast_free(marked_users);
1889 ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
1891 AST_LIST_TRAVERSE(&confs, cnf, list) {
1892 hr = (now - cnf->start) / 3600;
1893 min = ((now - cnf->start) % 3600) / 60;
1894 sec = (now - cnf->start) % 60;
1896 if (cnf->markedusers == 0) {
1897 ast_str_set(&marked_users, 0, "N/A ");
1899 ast_str_set(&marked_users, 0, "%4.4d", cnf->markedusers);
1901 ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users,
1902 ast_str_buffer(marked_users), hr, min, sec,
1903 cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
1905 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1914 total += cnf->users;
1916 AST_LIST_UNLOCK(&confs);
1918 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1920 ast_free(marked_users);
1923 if (a->argc == 3 || (a->argc == 4 && !strcasecmp(a->argv[3], STR_CONCISE))) {
1924 struct ao2_iterator user_iter;
1925 int concise = (a->argc == 4);
1927 /* List all the users in a conference */
1928 if (AST_LIST_EMPTY(&confs)) {
1930 ast_cli(a->fd, "No active MeetMe conferences.\n");
1934 /* Find the right conference */
1935 AST_LIST_LOCK(&confs);
1936 AST_LIST_TRAVERSE(&confs, cnf, list) {
1937 if (strcmp(cnf->confno, a->argv[2]) == 0) {
1943 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
1944 AST_LIST_UNLOCK(&confs);
1947 /* Show all the users */
1949 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
1950 while((user = ao2_iterator_next(&user_iter))) {
1951 hr = (now - user->jointime) / 3600;
1952 min = ((now - user->jointime) % 3600) / 60;
1953 sec = (now - user->jointime) % 60;
1955 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1957 S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
1958 S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<no name>"),
1959 ast_channel_name(user->chan),
1960 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "(Admin)" : "",
1961 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "(Listen only)" : "",
1962 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1963 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1964 istalking(user->talking), hr, min, sec);
1966 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1968 S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, ""),
1969 S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, ""),
1970 ast_channel_name(user->chan),
1971 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "1" : "",
1972 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "1" : "",
1973 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1974 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1975 user->talking, hr, min, sec);
1979 ao2_iterator_destroy(&user_iter);
1981 ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
1983 AST_LIST_UNLOCK(&confs);
1986 return CLI_SHOWUSAGE;
1990 static char *meetme_cmd_helper(struct ast_cli_args *a)
1992 /* Process the command */
1993 struct ast_str *cmdline;
1995 /* Max confno length */
1996 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
2000 ast_str_set(&cmdline, 0, "%s", a->argv[2]); /* Argv 2: conference number */
2001 if (strcasestr(a->argv[1], "lock")) {
2002 if (strcasecmp(a->argv[1], "lock") == 0) {
2004 ast_str_append(&cmdline, 0, ",L");
2007 ast_str_append(&cmdline, 0, ",l");
2009 } else if (strcasestr(a->argv[1], "mute")) {
2010 if (strcasecmp(a->argv[1], "mute") == 0) {
2012 if (strcasecmp(a->argv[3], "all") == 0) {
2013 ast_str_append(&cmdline, 0, ",N");
2015 ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);
2019 if (strcasecmp(a->argv[3], "all") == 0) {
2020 ast_str_append(&cmdline, 0, ",n");
2022 ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
2025 } else if (strcasecmp(a->argv[1], "kick") == 0) {
2026 if (strcasecmp(a->argv[3], "all") == 0) {
2028 ast_str_append(&cmdline, 0, ",K");
2030 /* Kick a single user */
2031 ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
2035 * Should never get here because it is already filtered by the
2039 return CLI_SHOWUSAGE;
2042 ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
2044 admin_exec(NULL, ast_str_buffer(cmdline));
2050 static char *meetme_lock_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2054 e->command = "meetme {lock|unlock}";
2056 "Usage: meetme lock|unlock <confno>\n"
2057 " Lock or unlock a conference to new users.\n";
2060 return complete_meetmecmd_lock(a->word, a->pos, a->n);
2064 return CLI_SHOWUSAGE;
2067 return meetme_cmd_helper(a);
2070 static char *meetme_kick_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2074 e->command = "meetme kick";
2076 "Usage: meetme kick <confno> all|<userno>\n"
2077 " Kick a conference or a user in a conference.\n";
2080 return complete_meetmecmd_mute_kick(a->line, a->word, a->pos, a->n);
2084 return CLI_SHOWUSAGE;
2087 return meetme_cmd_helper(a);
2090 static char *meetme_mute_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2094 e->command = "meetme {mute|unmute}";
2096 "Usage: meetme mute|unmute <confno> all|<userno>\n"
2097 " Mute or unmute a conference or a user in a conference.\n";
2100 return complete_meetmecmd_mute_kick(a->line, a->word, a->pos, a->n);
2104 return CLI_SHOWUSAGE;
2107 return meetme_cmd_helper(a);
2110 static const char *sla_hold_str(unsigned int hold_access)
2112 const char *hold = "Unknown";
2114 switch (hold_access) {
2118 case SLA_HOLD_PRIVATE:
2127 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2129 struct ao2_iterator i;
2130 struct sla_trunk *trunk;
2134 e->command = "sla show trunks";
2136 "Usage: sla show trunks\n"
2137 " This will list all trunks defined in sla.conf\n";
2144 "=============================================================\n"
2145 "=== Configured SLA Trunks ===================================\n"
2146 "=============================================================\n"
2148 i = ao2_iterator_init(sla_trunks, 0);
2149 for (; (trunk = ao2_iterator_next(&i)); ao2_ref(trunk, -1)) {
2150 struct sla_station_ref *station_ref;
2151 char ring_timeout[16] = "(none)";
2155 if (trunk->ring_timeout) {
2156 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
2159 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
2160 "=== Trunk Name: %s\n"
2161 "=== ==> Device: %s\n"
2162 "=== ==> AutoContext: %s\n"
2163 "=== ==> RingTimeout: %s\n"
2164 "=== ==> BargeAllowed: %s\n"
2165 "=== ==> HoldAccess: %s\n"
2166 "=== ==> Stations ...\n",
2167 trunk->name, trunk->device,
2168 S_OR(trunk->autocontext, "(none)"),
2170 trunk->barge_disabled ? "No" : "Yes",
2171 sla_hold_str(trunk->hold_access));
2173 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
2174 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
2177 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
2181 ao2_iterator_destroy(&i);
2182 ast_cli(a->fd, "=============================================================\n\n");
2187 static const char *trunkstate2str(enum sla_trunk_state state)
2189 #define S(e) case e: return # e;
2191 S(SLA_TRUNK_STATE_IDLE)
2192 S(SLA_TRUNK_STATE_RINGING)
2193 S(SLA_TRUNK_STATE_UP)
2194 S(SLA_TRUNK_STATE_ONHOLD)
2195 S(SLA_TRUNK_STATE_ONHOLD_BYME)
2197 return "Uknown State";
2201 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2203 struct ao2_iterator i;
2204 struct sla_station *station;
2208 e->command = "sla show stations";
2210 "Usage: sla show stations\n"
2211 " This will list all stations defined in sla.conf\n";
2218 "=============================================================\n"
2219 "=== Configured SLA Stations =================================\n"
2220 "=============================================================\n"
2222 i = ao2_iterator_init(sla_stations, 0);
2223 for (; (station = ao2_iterator_next(&i)); ao2_ref(station, -1)) {
2224 struct sla_trunk_ref *trunk_ref;
2225 char ring_timeout[16] = "(none)";
2226 char ring_delay[16] = "(none)";
2230 if (station->ring_timeout) {
2231 snprintf(ring_timeout, sizeof(ring_timeout),
2232 "%u", station->ring_timeout);
2234 if (station->ring_delay) {
2235 snprintf(ring_delay, sizeof(ring_delay),
2236 "%u", station->ring_delay);
2238 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
2239 "=== Station Name: %s\n"
2240 "=== ==> Device: %s\n"
2241 "=== ==> AutoContext: %s\n"
2242 "=== ==> RingTimeout: %s\n"
2243 "=== ==> RingDelay: %s\n"
2244 "=== ==> HoldAccess: %s\n"
2245 "=== ==> Trunks ...\n",
2246 station->name, station->device,
2247 S_OR(station->autocontext, "(none)"),
2248 ring_timeout, ring_delay,
2249 sla_hold_str(station->hold_access));
2250 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
2251 if (trunk_ref->ring_timeout) {
2252 snprintf(ring_timeout, sizeof(ring_timeout),
2253 "%u", trunk_ref->ring_timeout);
2255 strcpy(ring_timeout, "(none)");
2257 if (trunk_ref->ring_delay) {
2258 snprintf(ring_delay, sizeof(ring_delay),
2259 "%u", trunk_ref->ring_delay);
2261 strcpy(ring_delay, "(none)");
2264 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
2265 "=== ==> State: %s\n"
2266 "=== ==> RingTimeout: %s\n"
2267 "=== ==> RingDelay: %s\n",
2268 trunk_ref->trunk->name,
2269 trunkstate2str(trunk_ref->state),
2270 ring_timeout, ring_delay);
2272 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
2275 ao2_unlock(station);
2277 ao2_iterator_destroy(&i);
2278 ast_cli(a->fd, "============================================================\n"
2284 static struct ast_cli_entry cli_meetme[] = {
2285 AST_CLI_DEFINE(meetme_kick_cmd, "Kick a conference or a user in a conference."),
2286 AST_CLI_DEFINE(meetme_show_cmd, "List all conferences or a specific conference."),
2287 AST_CLI_DEFINE(meetme_lock_cmd, "Lock or unlock a conference to new users."),
2288 AST_CLI_DEFINE(meetme_mute_cmd, "Mute or unmute a conference or a user in a conference."),
2289 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
2290 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
2293 static void conf_flush(int fd, struct ast_channel *chan)
2297 /* read any frames that may be waiting on the channel
2301 struct ast_frame *f;
2303 /* when no frames are available, this will wait
2304 for 1 millisecond maximum
2306 while (ast_waitfor(chan, 1) > 0) {
2310 else /* channel was hung up or something else happened */
2315 /* flush any data sitting in the pseudo channel */
2316 x = DAHDI_FLUSH_ALL;
2317 if (ioctl(fd, DAHDI_FLUSH, &x))
2318 ast_log(LOG_WARNING, "Error flushing channel\n");
2322 /*! \brief Remove the conference from the list and free it.
2324 We assume that this was called while holding conflock. */
2325 static int conf_free(struct ast_conference *conf)
2328 struct announce_listitem *item;
2330 AST_LIST_REMOVE(&confs, conf, list);
2332 meetme_stasis_generate_msg(conf, NULL, NULL, meetme_end_type(), NULL);
2334 if (conf->recording == MEETME_RECORD_ACTIVE) {
2335 conf->recording = MEETME_RECORD_TERMINATE;
2336 AST_LIST_UNLOCK(&confs);
2339 AST_LIST_LOCK(&confs);
2340 if (conf->recording == MEETME_RECORD_OFF)
2342 AST_LIST_UNLOCK(&confs);
2346 for (x = 0; x < AST_FRAME_BITS; x++) {
2347 if (conf->transframe[x])
2348 ast_frfree(conf->transframe[x]);
2349 if (conf->transpath[x])
2350 ast_translator_free_path(conf->transpath[x]);
2352 if (conf->announcethread != AST_PTHREADT_NULL) {
2353 ast_mutex_lock(&conf->announcelistlock);
2354 conf->announcethread_stop = 1;
2355 ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
2356 ast_cond_signal(&conf->announcelist_addition);
2357 ast_mutex_unlock(&conf->announcelistlock);
2358 pthread_join(conf->announcethread, NULL);
2360 while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
2361 /* If it's a voicemail greeting file we don't want to remove it */
2363 ast_filedelete(item->namerecloc, NULL);
2367 ast_mutex_destroy(&conf->announcelistlock);
2370 if (conf->origframe)
2371 ast_frfree(conf->origframe);
2372 ast_hangup(conf->lchan);
2373 ast_hangup(conf->chan);
2376 if (conf->recordingfilename) {
2377 ast_free(conf->recordingfilename);
2379 if (conf->usercontainer) {
2380 ao2_ref(conf->usercontainer, -1);
2382 if (conf->recordingformat) {
2383 ast_free(conf->recordingformat);
2385 ast_mutex_destroy(&conf->playlock);
2386 ast_mutex_destroy(&conf->listenlock);
2387 ast_mutex_destroy(&conf->recordthreadlock);
2388 ast_mutex_destroy(&conf->announcethreadlock);
2394 static void conf_queue_dtmf(const struct ast_conference *conf,
2395 const struct ast_conf_user *sender, struct ast_frame *f)
2397 struct ast_conf_user *user;
2398 struct ao2_iterator user_iter;
2400 user_iter = ao2_iterator_init(conf->usercontainer, 0);
2401 while ((user = ao2_iterator_next(&user_iter))) {
2402 if (user == sender) {
2406 if (ast_write(user->chan, f) < 0)
2407 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", ast_channel_name(user->chan));
2410 ao2_iterator_destroy(&user_iter);
2413 static void sla_queue_event_full(enum sla_event_type type,
2414 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
2416 struct sla_event *event;
2418 if (sla.thread == AST_PTHREADT_NULL) {
2419 ao2_ref(station, -1);
2420 ao2_ref(trunk_ref, -1);
2424 if (!(event = ast_calloc(1, sizeof(*event)))) {
2425 ao2_ref(station, -1);
2426 ao2_ref(trunk_ref, -1);
2431 event->trunk_ref = trunk_ref;
2432 event->station = station;
2435 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
2439 ast_mutex_lock(&sla.lock);
2440 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
2441 ast_cond_signal(&sla.cond);
2442 ast_mutex_unlock(&sla.lock);
2445 static void sla_queue_event_nolock(enum sla_event_type type)
2447 sla_queue_event_full(type, NULL, NULL, 0);
2450 static void sla_queue_event(enum sla_event_type type)
2452 sla_queue_event_full(type, NULL, NULL, 1);
2455 /*! \brief Queue a SLA event from the conference */
2456 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
2457 struct ast_conference *conf)
2459 struct sla_station *station;
2460 struct sla_trunk_ref *trunk_ref = NULL;
2462 struct ao2_iterator i;
2464 trunk_name = ast_strdupa(conf->confno);
2465 strsep(&trunk_name, "_");
2466 if (ast_strlen_zero(trunk_name)) {
2467 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
2471 i = ao2_iterator_init(sla_stations, 0);
2472 while ((station = ao2_iterator_next(&i))) {
2474 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
2475 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name)) {
2476 ao2_ref(trunk_ref, 1);
2480 ao2_unlock(station);
2482 /* station reference given to sla_queue_event_full() */
2485 ao2_ref(station, -1);
2487 ao2_iterator_destroy(&i);
2490 ast_debug(1, "Trunk not found for event!\n");
2494 sla_queue_event_full(type, trunk_ref, station, 1);
2497 /*! \brief Decrement reference counts, as incremented by find_conf() */
2498 static int dispose_conf(struct ast_conference *conf)
2503 AST_LIST_LOCK(&confs);
2504 if (ast_atomic_dec_and_test(&conf->refcount)) {
2505 /* Take the conference room number out of an inuse state */
2506 if ((sscanf(conf->confno, "%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
2507 conf_map[confno_int] = 0;
2512 AST_LIST_UNLOCK(&confs);
2517 static int rt_extend_conf(const char *confno)
2519 char currenttime[32];
2523 struct ast_variable *var, *orig_var;
2532 ast_localtime(&now, &tm, NULL);
2533 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2535 var = ast_load_realtime("meetme", "confno",
2536 confno, "startTime<= ", currenttime,
2537 "endtime>= ", currenttime, NULL);
2541 /* Identify the specific RealTime conference */
2543 if (!strcasecmp(var->name, "bookid")) {
2544 ast_copy_string(bookid, var->value, sizeof(bookid));
2546 if (!strcasecmp(var->name, "endtime")) {
2547 ast_copy_string(endtime, var->value, sizeof(endtime));
2552 ast_variables_destroy(orig_var);
2554 ast_strptime(endtime, DATE_FORMAT, &tm);
2555 now = ast_mktime(&tm, NULL);
2557 now.tv_sec += extendby;
2559 ast_localtime(&now, &tm, NULL);
2560 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2561 strcat(currenttime, "0"); /* Seconds needs to be 00 */
2563 var = ast_load_realtime("meetme", "confno",
2564 confno, "startTime<= ", currenttime,
2565 "endtime>= ", currenttime, NULL);
2567 /* If there is no conflict with extending the conference, update the DB */
2569 ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
2570 ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
2575 ast_variables_destroy(var);
2579 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
2583 ast_channel_lock(chan);
2584 original_moh = ast_strdupa(ast_channel_musicclass(chan));
2585 ast_channel_musicclass_set(chan, musicclass);
2586 ast_channel_unlock(chan);
2588 ast_moh_start(chan, original_moh, NULL);
2590 ast_channel_lock(chan);
2591 ast_channel_musicclass_set(chan, original_moh);
2592 ast_channel_unlock(chan);
2595 static const char *get_announce_filename(enum announcetypes type)
2599 return "conf-hasleft";
2602 return "conf-hasjoin";
2609 static void *announce_thread(void *data)
2611 struct announce_listitem *current;
2612 struct ast_conference *conf = data;
2614 char filename[PATH_MAX] = "";
2615 AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
2616 AST_LIST_HEAD_INIT_NOLOCK(&local_list);
2618 while (!conf->announcethread_stop) {
2619 ast_mutex_lock(&conf->announcelistlock);
2620 if (conf->announcethread_stop) {
2621 ast_mutex_unlock(&conf->announcelistlock);
2624 if (AST_LIST_EMPTY(&conf->announcelist))
2625 ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
2627 AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
2628 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2630 ast_mutex_unlock(&conf->announcelistlock);
2631 if (conf->announcethread_stop) {
2635 for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
2636 ast_debug(1, "About to play %s\n", current->namerecloc);
2637 if (!ast_fileexists(current->namerecloc, NULL, NULL))
2639 if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
2640 if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
2641 res = ast_waitstream(current->confchan, "");
2643 ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
2644 if (!ast_streamfile(current->confchan, filename, current->language))
2645 ast_waitstream(current->confchan, "");
2648 if (current->announcetype == CONF_HASLEFT && current->announcetype && !current->vmrec) {
2649 /* only remove it if it isn't a VM recording file */
2650 ast_filedelete(current->namerecloc, NULL);
2655 /* thread marked to stop, clean up */
2656 while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
2657 /* only delete if it's a vm rec */
2658 if (!current->vmrec) {
2659 ast_filedelete(current->namerecloc, NULL);
2661 ao2_ref(current, -1);
2666 static int can_write(struct ast_channel *chan, struct ast_flags64 *confflags)
2668 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
2672 return (ast_channel_state(chan) == AST_STATE_UP);
2675 static void send_talking_event(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking)
2677 RAII_VAR(struct ast_json *, status_blob, status_to_json(talking), ast_json_unref);
2678 meetme_stasis_generate_msg(conf, chan, user, meetme_talking_type(), status_blob);
2681 static void set_user_talking(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor)
2683 int last_talking = user->talking;
2684 if (last_talking == talking)
2687 user->talking = talking;
2690 /* Check if talking state changed. Take care of -1 which means unmonitored */
2691 int was_talking = (last_talking > 0);
2692 int now_talking = (talking > 0);
2693 if (was_talking != now_talking) {
2694 send_talking_event(chan, conf, user, now_talking);
2699 static int user_set_hangup_cb(void *obj, void *check_admin_arg, int flags)
2701 struct ast_conf_user *user = obj;
2702 /* actual pointer contents of check_admin_arg is irrelevant */
2704 if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
2705 user->adminflags |= ADMINFLAG_HANGUP;
2710 static int user_set_kickme_cb(void *obj, void *check_admin_arg, int flags)
2712 struct ast_conf_user *user = obj;
2713 /* actual pointer contents of check_admin_arg is irrelevant */
2715 if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
2716 user->adminflags |= ADMINFLAG_KICKME;
2721 static int user_set_unmuted_cb(void *obj, void *check_admin_arg, int flags)
2723 struct ast_conf_user *user = obj;
2724 /* actual pointer contents of check_admin_arg is irrelevant */
2726 if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
2727 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
2732 static int user_set_muted_cb(void *obj, void *check_admin_arg, int flags)
2734 struct ast_conf_user *user = obj;
2735 /* actual pointer contents of check_admin_arg is irrelevant */
2737 if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
2738 user->adminflags |= ADMINFLAG_MUTED;
2747 MENU_ADMIN_EXTENDED,
2751 * \brief Processes menu options for the standard menu (accessible through the 's' option for app_meetme)
2753 * \param menu_mode a pointer to the currently active menu_mode.
2754 * \param dtmf a pointer to the dtmf value currently being processed against the menu.
2755 * \param conf the active conference for which the user has called the menu from.
2756 * \param confflags flags used by conf for various options
2757 * \param chan ast_channel belonging to the user who called the menu
2758 * \param user which meetme conference user invoked the menu
2760 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)
2763 case '1': /* Un/Mute */
2764 *menu_mode = MENU_DISABLED;
2766 /* user can only toggle the self-muted state */
2767 user->adminflags ^= ADMINFLAG_SELFMUTED;
2769 /* they can't override the admin mute state */
2770 if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2771 if (!ast_streamfile(chan, "conf-muted", ast_channel_language(chan))) {
2772 ast_waitstream(chan, "");
2775 if (!ast_streamfile(chan, "conf-unmuted", ast_channel_language(chan))) {
2776 ast_waitstream(chan, "");
2782 *menu_mode = MENU_DISABLED;
2783 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
2784 user->adminflags |= ADMINFLAG_T_REQUEST;
2787 if (user->adminflags & ADMINFLAG_T_REQUEST) {
2788 if (!ast_streamfile(chan, "beep", ast_channel_language(chan))) {
2789 ast_waitstream(chan, "");
2795 tweak_listen_volume(user, VOL_DOWN);
2798 /* Extend RT conference */
2800 rt_extend_conf(conf->confno);
2802 *menu_mode = MENU_DISABLED;
2806 tweak_listen_volume(user, VOL_UP);
2810 tweak_talk_volume(user, VOL_DOWN);
2814 *menu_mode = MENU_DISABLED;
2818 tweak_talk_volume(user, VOL_UP);
2822 *menu_mode = MENU_DISABLED;
2823 if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
2824 ast_waitstream(chan, "");
2831 * \brief Processes menu options for the adminstrator menu (accessible through the 's' option for app_meetme)
2833 * \param menu_mode a pointer to the currently active menu_mode.
2834 * \param dtmf a pointer to the dtmf value currently being processed against the menu.
2835 * \param conf the active conference for which the user has called the menu from.
2836 * \param confflags flags used by conf for various options
2837 * \param chan ast_channel belonging to the user who called the menu
2838 * \param user which meetme conference user invoked the menu
2840 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)
2843 case '1': /* Un/Mute */
2844 *menu_mode = MENU_DISABLED;
2845 /* for admin, change both admin and use flags */
2846 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
2847 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2849 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2852 if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2853 if (!ast_streamfile(chan, "conf-muted", ast_channel_language(chan))) {
2854 ast_waitstream(chan, "");
2857 if (!ast_streamfile(chan, "conf-unmuted", ast_channel_language(chan))) {
2858 ast_waitstream(chan, "");
2863 case '2': /* Un/Lock the Conference */
2864 *menu_mode = MENU_DISABLED;
2867 if (!ast_streamfile(chan, "conf-unlockednow", ast_channel_language(chan))) {
2868 ast_waitstream(chan, "");
2872 if (!ast_streamfile(chan, "conf-lockednow", ast_channel_language(chan))) {
2873 ast_waitstream(chan, "");
2878 case '3': /* Eject last user */
2880 struct ast_conf_user *usr = NULL;
2882 ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
2883 *menu_mode = MENU_DISABLED;
2884 usr = ao2_find(conf->usercontainer, &max_no, 0);
2885 if ((ast_channel_name(usr->chan) == ast_channel_name(chan)) || ast_test_flag64(&usr->userflags, CONFFLAG_ADMIN)) {
2886 if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
2887 ast_waitstream(chan, "");
2890 usr->adminflags |= ADMINFLAG_KICKME;
2893 ast_stopstream(chan);
2898 tweak_listen_volume(user, VOL_DOWN);
2902 /* Extend RT conference */
2904 if (!rt_extend_conf(conf->confno)) {
2905 if (!ast_streamfile(chan, "conf-extended", ast_channel_language(chan))) {
2906 ast_waitstream(chan, "");
2909 if (!ast_streamfile(chan, "conf-nonextended", ast_channel_language(chan))) {
2910 ast_waitstream(chan, "");
2913 ast_stopstream(chan);
2915 *menu_mode = MENU_DISABLED;
2919 tweak_listen_volume(user, VOL_UP);
2923 tweak_talk_volume(user, VOL_DOWN);
2927 if (!ast_streamfile(chan, "conf-adminmenu-menu8", ast_channel_language(chan))) {
2928 /* 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. */
2929 *dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2930 ast_stopstream(chan);
2932 *menu_mode = MENU_ADMIN_EXTENDED;
2936 tweak_talk_volume(user, VOL_UP);
2939 *menu_mode = MENU_DISABLED;
2940 /* Play an error message! */
2941 if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
2942 ast_waitstream(chan, "");
2950 * \brief Processes menu options for the extended administrator menu (accessible through option 8 on the administrator menu)
2952 * \param menu_mode a pointer to the currently active menu_mode.
2953 * \param dtmf a pointer to the dtmf value currently being processed against the menu.
2954 * \param conf the active conference for which the user has called the menu from.
2955 * \param confflags flags used by conf for various options
2956 * \param chan ast_channel belonging to the user who called the menu
2957 * \param user which meetme conference user invoked the menu
2958 * \param recordingtmp character buffer which may hold the name of the conference recording file
2960 static void meetme_menu_admin_extended(enum menu_modes *menu_mode, int *dtmf,
2961 struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan,
2962 struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size,
2963 struct ast_format_cap *cap_slin)
2968 struct ao2_iterator user_iter;
2969 struct ast_conf_user *usr = NULL;
2972 case '1': /* *81 Roll call */