2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2007, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * SLA Implementation by:
9 * Russell Bryant <russell@digium.com>
11 * See http://www.asterisk.org for more information about
12 * the Asterisk project. Please do not directly contact
13 * any of the maintainers of this project for assistance;
14 * the project provides a web site, mailing lists and IRC
15 * channels for your use.
17 * This program is free software, distributed under the terms of
18 * the GNU General Public License Version 2. See the LICENSE file
19 * at the top of the source tree.
24 * \brief Meet me conference bridge and Shared Line Appearances
26 * \author Mark Spencer <markster@digium.com>
27 * \author (SLA) Russell Bryant <russell@digium.com>
29 * \ingroup applications
33 <depend>dahdi</depend>
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
40 #include <dahdi/user.h>
42 #include "asterisk/lock.h"
43 #include "asterisk/file.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/pbx.h"
46 #include "asterisk/module.h"
47 #include "asterisk/config.h"
48 #include "asterisk/app.h"
49 #include "asterisk/dsp.h"
50 #include "asterisk/musiconhold.h"
51 #include "asterisk/manager.h"
52 #include "asterisk/cli.h"
53 #include "asterisk/say.h"
54 #include "asterisk/utils.h"
55 #include "asterisk/translate.h"
56 #include "asterisk/ulaw.h"
57 #include "asterisk/astobj2.h"
58 #include "asterisk/devicestate.h"
59 #include "asterisk/dial.h"
60 #include "asterisk/causes.h"
61 #include "asterisk/paths.h"
62 #include "asterisk/data.h"
63 #include "asterisk/test.h"
69 <application name="MeetMe" language="en_US">
71 MeetMe conference bridge.
74 <parameter name="confno">
75 <para>The conference number</para>
77 <parameter name="options">
80 <para>Set admin mode.</para>
83 <para>Set marked mode.</para>
86 <para>Run AGI script specified in <variable>MEETME_AGI_BACKGROUND</variable>
87 Default: <literal>conf-background.agi</literal>.</para>
88 <note><para>This does not work with non-DAHDI channels in the same
89 conference).</para></note>
92 <para>Announce user(s) count on joining a conference.</para>
95 <para>Continue in dialplan when kicked out of conference.</para>
98 <para>Dynamically add conference.</para>
101 <para>Dynamically add conference, prompting for a PIN.</para>
104 <para>Select an empty conference.</para>
107 <para>Select an empty pinless conference.</para>
110 <para>Pass DTMF through the conference.</para>
113 <argument name="x" required="true">
114 <para>The file to playback</para>
116 <para>Play an intro announcement in conference.</para>
119 <para>Announce user join/leave with review.</para>
122 <para>Announce user join/leave without review.</para>
125 <para>Set listen only mode (Listen only, no talking).</para>
128 <para>Set initially muted.</para>
130 <option name="M" hasparams="optional">
131 <para>Enable music on hold when the conference has a single caller. Optionally,
132 specify a musiconhold class to use. If one is not provided, it will use the
133 channel's currently set music class, or <literal>default</literal>.</para>
134 <argument name="class" required="true" />
137 <para>Set talker optimization - treats talkers who aren't speaking as
138 being muted, meaning (a) No encode is done on transmission and (b)
139 Received audio that is not registered as talking is omitted causing no
140 buildup in background noise.</para>
142 <option name="p" hasparams="optional">
143 <para>Allow user to exit the conference by pressing <literal>#</literal> (default)
144 or any of the defined keys. If keys contain <literal>*</literal> this will override
145 option <literal>s</literal>. The key used is set to channel variable
146 <variable>MEETME_EXIT_KEY</variable>.</para>
147 <argument name="keys" required="true" />
150 <para>Always prompt for the pin even if it is specified.</para>
153 <para>Quiet mode (don't play enter/leave sounds).</para>
156 <para>Record conference (records as <variable>MEETME_RECORDINGFILE</variable>
157 using format <variable>MEETME_RECORDINGFORMAT</variable>. Default filename is
158 <literal>meetme-conf-rec-${CONFNO}-${UNIQUEID}</literal> and the default format is
162 <para>Present menu (user or admin) when <literal>*</literal> is received
163 (send to menu).</para>
166 <para>Set talk only mode. (Talk only, no listening).</para>
169 <para>Set talker detection (sent to manager interface and meetme list).</para>
171 <option name="v" hasparams="optional">
172 <para>Announce when a user is joining or leaving the conference. Use the voicemail greeting as the announcement.
173 If the i or I options are set, the application will fall back to them if no voicemail greeting can be found.</para>
174 <argument name="mailbox@[context]" required="true">
175 <para>The mailbox and voicemail context to play from. If no context provided, assumed context is default.</para>
178 <option name="w" hasparams="optional">
179 <para>Wait until the marked user enters the conference.</para>
180 <argument name="secs" required="true" />
183 <para>Close the conference when last marked user exits</para>
186 <para>Allow user to exit the conference by entering a valid single digit
187 extension <variable>MEETME_EXIT_CONTEXT</variable> or the current context
188 if that variable is not defined.</para>
191 <para>Do not play message when first person enters</para>
194 <para>Kick the user <replaceable>x</replaceable> seconds <emphasis>after</emphasis> he entered into
195 the conference.</para>
196 <argument name="x" required="true" />
198 <option name="L" argsep=":">
199 <para>Limit the conference to <replaceable>x</replaceable> ms. Play a warning when
200 <replaceable>y</replaceable> ms are left. Repeat the warning every <replaceable>z</replaceable> ms.
201 The following special variables can be used with this option:</para>
203 <variable name="CONF_LIMIT_TIMEOUT_FILE">
204 <para>File to play when time is up.</para>
206 <variable name="CONF_LIMIT_WARNING_FILE">
207 <para>File to play as warning if <replaceable>y</replaceable> is defined. The
208 default is to say the time remaining.</para>
211 <argument name="x" />
212 <argument name="y" />
213 <argument name="z" />
217 <parameter name="pin" />
220 <para>Enters the user into a specified MeetMe conference. If the <replaceable>confno</replaceable>
221 is omitted, the user will be prompted to enter one. User can exit the conference by hangup, or
222 if the <literal>p</literal> option is specified, by pressing <literal>#</literal>.</para>
223 <note><para>The DAHDI kernel modules and at least one hardware driver (or dahdi_dummy)
224 must be present for conferencing to operate properly. In addition, the chan_dahdi channel driver
225 must be loaded for the <literal>i</literal> and <literal>r</literal> options to operate at
229 <ref type="application">MeetMeCount</ref>
230 <ref type="application">MeetMeAdmin</ref>
231 <ref type="application">MeetMeChannelAdmin</ref>
234 <application name="MeetMeCount" language="en_US">
236 MeetMe participant count.
239 <parameter name="confno" required="true">
240 <para>Conference number.</para>
242 <parameter name="var" />
245 <para>Plays back the number of users in the specified MeetMe conference.
246 If <replaceable>var</replaceable> is specified, playback will be skipped and the value
247 will be returned in the variable. Upon application completion, MeetMeCount will hangup
248 the channel, unless priority <literal>n+1</literal> exists, in which case priority progress will
252 <ref type="application">MeetMe</ref>
255 <application name="MeetMeAdmin" language="en_US">
257 MeetMe conference administration.
260 <parameter name="confno" required="true" />
261 <parameter name="command" required="true">
264 <para>Eject last user that joined.</para>
267 <para>Extend conference end time, if scheduled.</para>
270 <para>Kick one user out of conference.</para>
273 <para>Kick all users out of conference.</para>
276 <para>Unlock conference.</para>
279 <para>Lock conference.</para>
282 <para>Unmute one user.</para>
285 <para>Mute one user.</para>
288 <para>Unmute all users in the conference.</para>
291 <para>Mute all non-admin users in the conference.</para>
294 <para>Reset one user's volume settings.</para>
297 <para>Reset all users volume settings.</para>
300 <para>Lower entire conference speaking volume.</para>
303 <para>Raise entire conference speaking volume.</para>
306 <para>Lower one user's talk volume.</para>
309 <para>Raise one user's talk volume.</para>
312 <para>Lower one user's listen volume.</para>
315 <para>Raise one user's listen volume.</para>
318 <para>Lower entire conference listening volume.</para>
321 <para>Raise entire conference listening volume.</para>
325 <parameter name="user" />
328 <para>Run admin <replaceable>command</replaceable> for conference <replaceable>confno</replaceable>.</para>
329 <para>Will additionally set the variable <variable>MEETMEADMINSTATUS</variable> with one of
330 the following values:</para>
332 <variable name="MEETMEADMINSTATUS">
333 <value name="NOPARSE">
336 <value name="NOTFOUND">
337 User specified was not found.
339 <value name="FAILED">
340 Another failure occurred.
343 The operation was completed successfully.
349 <ref type="application">MeetMe</ref>
352 <application name="MeetMeChannelAdmin" language="en_US">
354 MeetMe conference Administration (channel specific).
357 <parameter name="channel" required="true" />
358 <parameter name="command" required="true">
361 <para>Kick the specified user out of the conference he is in.</para>
364 <para>Unmute the specified user.</para>
367 <para>Mute the specified user.</para>
373 <para>Run admin <replaceable>command</replaceable> for a specific
374 <replaceable>channel</replaceable> in any coference.</para>
377 <application name="SLAStation" language="en_US">
379 Shared Line Appearance Station.
382 <parameter name="station" required="true">
383 <para>Station name</para>
387 <para>This application should be executed by an SLA station. The argument depends
388 on how the call was initiated. If the phone was just taken off hook, then the argument
389 <replaceable>station</replaceable> should be just the station name. If the call was
390 initiated by pressing a line key, then the station name should be preceded by an underscore
391 and the trunk name associated with that line button.</para>
392 <para>For example: <literal>station1_line1</literal></para>
393 <para>On exit, this application will set the variable <variable>SLASTATION_STATUS</variable> to
394 one of the following values:</para>
396 <variable name="SLASTATION_STATUS">
397 <value name="FAILURE" />
398 <value name="CONGESTION" />
399 <value name="SUCCESS" />
404 <application name="SLATrunk" language="en_US">
406 Shared Line Appearance Trunk.
409 <parameter name="trunk" required="true">
410 <para>Trunk name</para>
412 <parameter name="options">
414 <option name="M" hasparams="optional">
415 <para>Play back the specified MOH <replaceable>class</replaceable>
416 instead of ringing</para>
417 <argument name="class" required="true" />
423 <para>This application should be executed by an SLA trunk on an inbound call. The channel calling
424 this application should correspond to the SLA trunk with the name <replaceable>trunk</replaceable>
425 that is being passed as an argument.</para>
426 <para>On exit, this application will set the variable <variable>SLATRUNK_STATUS</variable> to
427 one of the following values:</para>
429 <variable name="SLATRUNK_STATUS">
430 <value name="FAILURE" />
431 <value name="SUCCESS" />
432 <value name="UNANSWERED" />
433 <value name="RINGTIMEOUT" />
438 <function name="MEETME_INFO" language="en_US">
440 Query a given conference of various properties.
443 <parameter name="keyword" required="true">
444 <para>Options:</para>
447 <para>Boolean of whether the corresponding conference is locked.</para>
449 <enum name="parties">
450 <para>Number of parties in a given conference</para>
452 <enum name="activity">
453 <para>Duration of conference in seconds.</para>
455 <enum name="dynamic">
456 <para>Boolean of whether the corresponding conference is dynamic.</para>
460 <parameter name="confno" required="true">
461 <para>Conference number to retrieve information from.</para>
466 <ref type="application">MeetMe</ref>
467 <ref type="application">MeetMeCount</ref>
468 <ref type="application">MeetMeAdmin</ref>
469 <ref type="application">MeetMeChannelAdmin</ref>
472 <manager name="MeetmeMute" language="en_US">
477 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
478 <parameter name="Meetme" required="true" />
479 <parameter name="Usernum" required="true" />
484 <manager name="MeetmeUnmute" language="en_US">
486 Unmute a Meetme user.
489 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
490 <parameter name="Meetme" required="true" />
491 <parameter name="Usernum" required="true" />
496 <manager name="MeetmeList" language="en_US">
498 List participants in a conference.
501 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
502 <parameter name="Conference" required="false">
503 <para>Conference number.</para>
507 <para>Lists all users in a particular MeetMe conference.
508 MeetmeList will follow as separate events, followed by a final event called
509 MeetmeListComplete.</para>
512 <manager name="MeetmeListRooms" language="en_US">
514 List active conferences.
517 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
520 <para>Lists data about all active conferences.
521 MeetmeListRooms will follow as separate events, followed by a final event called
522 MeetmeListRoomsComplete.</para>
527 #define CONFIG_FILE_NAME "meetme.conf"
528 #define SLA_CONFIG_FILE "sla.conf"
530 /*! each buffer is 20ms, so this is 640ms total */
531 #define DEFAULT_AUDIO_BUFFERS 32
533 /*! String format for scheduled conferences */
534 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
537 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
538 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
539 ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */
540 /*! User has requested to speak */
541 ADMINFLAG_T_REQUEST = (1 << 4),
544 #define MEETME_DELAYDETECTTALK 300
545 #define MEETME_DELAYDETECTENDTALK 1000
547 #define AST_FRAME_BITS 32
554 enum entrance_sound {
559 enum recording_state {
561 MEETME_RECORD_STARTED,
562 MEETME_RECORD_ACTIVE,
563 MEETME_RECORD_TERMINATE
566 #define CONF_SIZE 320
569 /*! user has admin access on the conference */
570 CONFFLAG_ADMIN = (1 << 0),
571 /*! If set the user can only receive audio from the conference */
572 CONFFLAG_MONITOR = (1 << 1),
573 /*! If set asterisk will exit conference when key defined in p() option is pressed */
574 CONFFLAG_KEYEXIT = (1 << 2),
575 /*! If set asterisk will provide a menu to the user when '*' is pressed */
576 CONFFLAG_STARMENU = (1 << 3),
577 /*! If set the use can only send audio to the conference */
578 CONFFLAG_TALKER = (1 << 4),
579 /*! If set there will be no enter or leave sounds */
580 CONFFLAG_QUIET = (1 << 5),
581 /*! If set, when user joins the conference, they will be told the number
582 * of users that are already in */
583 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
584 /*! Set to run AGI Script in Background */
585 CONFFLAG_AGI = (1 << 7),
586 /*! Set to have music on hold when user is alone in conference */
587 CONFFLAG_MOH = (1 << 8),
588 /*! If set the MeetMe will return if all marked with this flag left */
589 CONFFLAG_MARKEDEXIT = (1 << 9),
590 /*! If set, the MeetMe will wait until a marked user enters */
591 CONFFLAG_WAITMARKED = (1 << 10),
592 /*! If set, the MeetMe will exit to the specified context */
593 CONFFLAG_EXIT_CONTEXT = (1 << 11),
594 /*! If set, the user will be marked */
595 CONFFLAG_MARKEDUSER = (1 << 12),
596 /*! If set, user will be ask record name on entry of conference */
597 CONFFLAG_INTROUSER = (1 << 13),
598 /*! If set, the MeetMe will be recorded */
599 CONFFLAG_RECORDCONF = (1<< 14),
600 /*! If set, the user will be monitored if the user is talking or not */
601 CONFFLAG_MONITORTALKER = (1 << 15),
602 CONFFLAG_DYNAMIC = (1 << 16),
603 CONFFLAG_DYNAMICPIN = (1 << 17),
604 CONFFLAG_EMPTY = (1 << 18),
605 CONFFLAG_EMPTYNOPIN = (1 << 19),
606 CONFFLAG_ALWAYSPROMPT = (1 << 20),
607 /*! If set, treat talking users as muted users */
608 CONFFLAG_OPTIMIZETALKER = (1 << 21),
609 /*! If set, won't speak the extra prompt when the first person
610 * enters the conference */
611 CONFFLAG_NOONLYPERSON = (1 << 22),
612 /*! If set, user will be asked to record name on entry of conference
614 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
615 /*! If set, the user will be initially self-muted */
616 CONFFLAG_STARTMUTED = (1 << 24),
617 /*! Pass DTMF through the conference */
618 CONFFLAG_PASS_DTMF = (1 << 25),
619 CONFFLAG_SLA_STATION = (1 << 26),
620 CONFFLAG_SLA_TRUNK = (1 << 27),
621 /*! If set, the user should continue in the dialplan if kicked out */
622 CONFFLAG_KICK_CONTINUE = (1 << 28),
623 CONFFLAG_DURATION_STOP = (1 << 29),
624 CONFFLAG_DURATION_LIMIT = (1 << 30),
625 /*! Do not write any audio to this channel until the state is up. */
626 CONFFLAG_NO_AUDIO_UNTIL_UP = (1 << 31),
629 /* These flags are defined separately because we ran out of bits that an enum can be used to represent.
630 If you add new flags, be sure to do it in the same way that CONFFLAG_INTROMSG is. */
631 #define CONFFLAG_INTROMSG ((uint64_t)1 << 32) /*!< If set play an intro announcement at start of conference */
632 #define CONFFLAG_INTROUSER_VMREC ((uint64_t)1 << 33)
635 OPT_ARG_WAITMARKED = 0,
636 OPT_ARG_EXITKEYS = 1,
637 OPT_ARG_DURATION_STOP = 2,
638 OPT_ARG_DURATION_LIMIT = 3,
639 OPT_ARG_MOH_CLASS = 4,
640 OPT_ARG_INTROMSG = 5,
641 OPT_ARG_INTROUSER_VMREC = 6,
642 OPT_ARG_ARRAY_SIZE = 7,
645 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
646 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
647 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
648 AST_APP_OPTION('b', CONFFLAG_AGI ),
649 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
650 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
651 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
652 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
653 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
654 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
655 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
656 AST_APP_OPTION_ARG('G', CONFFLAG_INTROMSG, OPT_ARG_INTROMSG ),
657 AST_APP_OPTION_ARG('v', CONFFLAG_INTROUSER_VMREC , OPT_ARG_INTROUSER_VMREC),
658 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
659 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
660 AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
661 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
662 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
663 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
664 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
665 AST_APP_OPTION('q', CONFFLAG_QUIET ),
666 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
667 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
668 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
669 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
670 AST_APP_OPTION('t', CONFFLAG_TALKER ),
671 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
672 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
673 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
674 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
675 AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
676 AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
679 static const char * const app = "MeetMe";
680 static const char * const app2 = "MeetMeCount";
681 static const char * const app3 = "MeetMeAdmin";
682 static const char * const app4 = "MeetMeChannelAdmin";
683 static const char * const slastation_app = "SLAStation";
684 static const char * const slatrunk_app = "SLATrunk";
686 /* Lookup RealTime conferences based on confno and current time */
687 static int rt_schedule;
688 static int fuzzystart;
689 static int earlyalert;
693 /*! Log participant count to the RealTime backend */
694 static int rt_log_members;
696 #define MAX_CONFNUM 80
698 #define OPTIONS_LEN 100
700 /* Enough space for "<conference #>,<pin>,<admin pin>" followed by a 0 byte. */
701 #define MAX_SETTINGS (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)
708 struct announce_listitem {
709 AST_LIST_ENTRY(announce_listitem) entry;
710 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
711 char language[MAX_LANGUAGE];
712 struct ast_channel *confchan;
715 enum announcetypes announcetype;
718 /*! \brief The MeetMe Conference object */
719 struct ast_conference {
720 ast_mutex_t playlock; /*!< Conference specific lock (players) */
721 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
722 char confno[MAX_CONFNUM]; /*!< Conference */
723 struct ast_channel *chan; /*!< Announcements channel */
724 struct ast_channel *lchan; /*!< Listen/Record channel */
725 int fd; /*!< Announcements fd */
726 int dahdiconf; /*!< DAHDI Conf # */
727 int users; /*!< Number of active users */
728 int markedusers; /*!< Number of marked users */
729 int maxusers; /*!< Participant limit if scheduled */
730 int endalert; /*!< When to play conf ending message */
731 time_t start; /*!< Start time (s) */
732 int refcount; /*!< reference count of usage */
733 enum recording_state recording:2; /*!< recording status */
734 unsigned int isdynamic:1; /*!< Created on the fly? */
735 unsigned int locked:1; /*!< Is the conference locked? */
736 unsigned int gmuted:1; /*!< Is the conference globally muted? (all non-admins) */
737 pthread_t recordthread; /*!< thread for recording */
738 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
739 pthread_attr_t attr; /*!< thread attribute */
740 char *recordingfilename; /*!< Filename to record the Conference into */
741 char *recordingformat; /*!< Format to record the Conference in */
742 char pin[MAX_PIN]; /*!< If protected by a PIN */
743 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
745 long endtime; /*!< When to end the conf if scheduled */
746 const char *useropts; /*!< RealTime user flags */
747 const char *adminopts; /*!< RealTime moderator flags */
748 const char *bookid; /*!< RealTime conference id */
749 struct ast_frame *transframe[32];
750 struct ast_frame *origframe;
751 struct ast_trans_pvt *transpath[32];
752 struct ao2_container *usercontainer;
753 AST_LIST_ENTRY(ast_conference) list;
754 /* announce_thread related data */
755 pthread_t announcethread;
756 ast_mutex_t announcethreadlock;
757 unsigned int announcethread_stop:1;
758 ast_cond_t announcelist_addition;
759 AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
760 ast_mutex_t announcelistlock;
763 static AST_LIST_HEAD_STATIC(confs, ast_conference);
765 static unsigned int conf_map[1024] = {0, };
768 int desired; /*!< Desired volume adjustment */
769 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
772 /*! \brief The MeetMe User object */
773 struct ast_conf_user {
774 int user_no; /*!< User Number */
775 struct ast_flags64 userflags; /*!< Flags as set in the conference */
776 int adminflags; /*!< Flags set by the Admin */
777 struct ast_channel *chan; /*!< Connected channel */
778 int talking; /*!< Is user talking */
779 int dahdichannel; /*!< Is a DAHDI channel */
780 char usrvalue[50]; /*!< Custom User Value */
781 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
782 time_t jointime; /*!< Time the user joined the conference */
783 time_t kicktime; /*!< Time the user will be kicked from the conference */
784 struct timeval start_time; /*!< Time the user entered into the conference */
785 long timelimit; /*!< Time limit for the user to be in the conference L(x:y:z) */
786 long play_warning; /*!< Play a warning when 'y' ms are left */
787 long warning_freq; /*!< Repeat the warning every 'z' ms */
788 const char *warning_sound; /*!< File to play as warning if 'y' is defined */
789 const char *end_sound; /*!< File to play when time is up. */
791 struct volume listen;
792 AST_LIST_ENTRY(ast_conf_user) list;
795 enum sla_which_trunk_refs {
800 enum sla_trunk_state {
801 SLA_TRUNK_STATE_IDLE,
802 SLA_TRUNK_STATE_RINGING,
804 SLA_TRUNK_STATE_ONHOLD,
805 SLA_TRUNK_STATE_ONHOLD_BYME,
808 enum sla_hold_access {
809 /*! This means that any station can put it on hold, and any station
810 * can retrieve the call from hold. */
812 /*! This means that only the station that put the call on hold may
813 * retrieve it from hold. */
817 struct sla_trunk_ref;
820 AST_RWLIST_ENTRY(sla_station) entry;
821 AST_DECLARE_STRING_FIELDS(
822 AST_STRING_FIELD(name);
823 AST_STRING_FIELD(device);
824 AST_STRING_FIELD(autocontext);
826 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
827 struct ast_dial *dial;
828 /*! Ring timeout for this station, for any trunk. If a ring timeout
829 * is set for a specific trunk on this station, that will take
830 * priority over this value. */
831 unsigned int ring_timeout;
832 /*! Ring delay for this station, for any trunk. If a ring delay
833 * is set for a specific trunk on this station, that will take
834 * priority over this value. */
835 unsigned int ring_delay;
836 /*! This option uses the values in the sla_hold_access enum and sets the
837 * access control type for hold on this station. */
838 unsigned int hold_access:1;
839 /*! Use count for inside sla_station_exec */
840 unsigned int ref_count;
843 struct sla_station_ref {
844 AST_LIST_ENTRY(sla_station_ref) entry;
845 struct sla_station *station;
849 AST_RWLIST_ENTRY(sla_trunk) entry;
850 AST_DECLARE_STRING_FIELDS(
851 AST_STRING_FIELD(name);
852 AST_STRING_FIELD(device);
853 AST_STRING_FIELD(autocontext);
855 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
856 /*! Number of stations that use this trunk */
857 unsigned int num_stations;
858 /*! Number of stations currently on a call with this trunk */
859 unsigned int active_stations;
860 /*! Number of stations that have this trunk on hold. */
861 unsigned int hold_stations;
862 struct ast_channel *chan;
863 unsigned int ring_timeout;
864 /*! If set to 1, no station will be able to join an active call with
866 unsigned int barge_disabled:1;
867 /*! This option uses the values in the sla_hold_access enum and sets the
868 * access control type for hold on this trunk. */
869 unsigned int hold_access:1;
870 /*! Whether this trunk is currently on hold, meaning that once a station
871 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
872 unsigned int on_hold:1;
873 /*! Use count for inside sla_trunk_exec */
874 unsigned int ref_count;
877 struct sla_trunk_ref {
878 AST_LIST_ENTRY(sla_trunk_ref) entry;
879 struct sla_trunk *trunk;
880 enum sla_trunk_state state;
881 struct ast_channel *chan;
882 /*! Ring timeout to use when this trunk is ringing on this specific
883 * station. This takes higher priority than a ring timeout set at
884 * the station level. */
885 unsigned int ring_timeout;
886 /*! Ring delay to use when this trunk is ringing on this specific
887 * station. This takes higher priority than a ring delay set at
888 * the station level. */
889 unsigned int ring_delay;
892 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
893 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
895 static const char sla_registrar[] = "SLA";
897 /*! \brief Event types that can be queued up for the SLA thread */
898 enum sla_event_type {
899 /*! A station has put the call on hold */
901 /*! The state of a dial has changed */
902 SLA_EVENT_DIAL_STATE,
903 /*! The state of a ringing trunk has changed */
904 SLA_EVENT_RINGING_TRUNK,
905 /*! A reload of configuration has been requested */
907 /*! Poke the SLA thread so it can check if it can perform a reload */
908 SLA_EVENT_CHECK_RELOAD,
912 enum sla_event_type type;
913 struct sla_station *station;
914 struct sla_trunk_ref *trunk_ref;
915 AST_LIST_ENTRY(sla_event) entry;
918 /*! \brief A station that failed to be dialed
919 * \note Only used by the SLA thread. */
920 struct sla_failed_station {
921 struct sla_station *station;
922 struct timeval last_try;
923 AST_LIST_ENTRY(sla_failed_station) entry;
926 /*! \brief A trunk that is ringing */
927 struct sla_ringing_trunk {
928 struct sla_trunk *trunk;
929 /*! The time that this trunk started ringing */
930 struct timeval ring_begin;
931 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
932 AST_LIST_ENTRY(sla_ringing_trunk) entry;
935 enum sla_station_hangup {
936 SLA_STATION_HANGUP_NORMAL,
937 SLA_STATION_HANGUP_TIMEOUT,
940 /*! \brief A station that is ringing */
941 struct sla_ringing_station {
942 struct sla_station *station;
943 /*! The time that this station started ringing */
944 struct timeval ring_begin;
945 AST_LIST_ENTRY(sla_ringing_station) entry;
949 * \brief A structure for data used by the sla thread
952 /*! The SLA thread ID */
956 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
957 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
958 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
959 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
961 /*! Attempt to handle CallerID, even though it is known not to work
962 * properly in some situations. */
963 unsigned int attempt_callerid:1;
964 /*! A reload has been requested */
965 unsigned int reload:1;
967 .thread = AST_PTHREADT_NULL,
970 /*! \brief The number of audio buffers to be allocated on pseudo channels
971 * when in a conference */
972 static int audio_buffers;
974 /*! \brief Map 'volume' levels from -5 through +5 into decibel (dB)
975 * settings for channel drivers.
977 * \note these are not a straight linear-to-dB
978 * conversion... the numbers have been modified
979 * to give the user a better level of adjustability.
981 static const char gain_map[] = {
996 static int admin_exec(struct ast_channel *chan, const char *data);
997 static void *recordthread(void *args);
999 static const char *istalking(int x)
1004 return "(unmonitored)";
1006 return "(not talking)";
1009 static int careful_write(int fd, unsigned char *data, int len, int block)
1016 x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
1017 res = ioctl(fd, DAHDI_IOMUX, &x);
1021 res = write(fd, data, len);
1023 if (errno != EAGAIN) {
1024 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
1036 static int set_talk_volume(struct ast_conf_user *user, int volume)
1040 /* attempt to make the adjustment in the channel driver;
1041 if successful, don't adjust in the frame reading routine
1043 gain_adjust = gain_map[volume + 5];
1045 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1048 static int set_listen_volume(struct ast_conf_user *user, int volume)
1052 /* attempt to make the adjustment in the channel driver;
1053 if successful, don't adjust in the frame reading routine
1055 gain_adjust = gain_map[volume + 5];
1057 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1060 static void tweak_volume(struct volume *vol, enum volume_action action)
1064 switch (vol->desired) {
1079 switch (vol->desired) {
1095 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
1097 tweak_volume(&user->talk, action);
1098 /* attempt to make the adjustment in the channel driver;
1099 if successful, don't adjust in the frame reading routine
1101 if (!set_talk_volume(user, user->talk.desired))
1102 user->talk.actual = 0;
1104 user->talk.actual = user->talk.desired;
1107 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
1109 tweak_volume(&user->listen, action);
1110 /* attempt to make the adjustment in the channel driver;
1111 if successful, don't adjust in the frame reading routine
1113 if (!set_listen_volume(user, user->listen.desired))
1114 user->listen.actual = 0;
1116 user->listen.actual = user->listen.desired;
1119 static void reset_volumes(struct ast_conf_user *user)
1121 signed char zero_volume = 0;
1123 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
1124 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
1127 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
1129 unsigned char *data;
1133 if (!ast_check_hangup(chan))
1134 res = ast_autoservice_start(chan);
1136 AST_LIST_LOCK(&confs);
1141 len = sizeof(enter);
1145 len = sizeof(leave);
1152 careful_write(conf->fd, data, len, 1);
1155 AST_LIST_UNLOCK(&confs);
1158 ast_autoservice_stop(chan);
1161 static int user_no_cmp(void *obj, void *arg, int flags)
1163 struct ast_conf_user *user = obj;
1166 if (user->user_no == *user_no) {
1167 return (CMP_MATCH | CMP_STOP);
1173 static int user_max_cmp(void *obj, void *arg, int flags)
1175 struct ast_conf_user *user = obj;
1178 if (user->user_no > *max_no) {
1179 *max_no = user->user_no;
1186 * \brief Find or create a conference
1188 * \param confno The conference name/number
1189 * \param pin The regular user pin
1190 * \param pinadmin The admin pin
1191 * \param make Make the conf if it doesn't exist
1192 * \param dynamic Mark the newly created conference as dynamic
1193 * \param refcount How many references to mark on the conference
1194 * \param chan The asterisk channel
1196 * \return A pointer to the conference struct, or NULL if it wasn't found and
1197 * make or dynamic were not set.
1199 static struct ast_conference *build_conf(const char *confno, const char *pin,
1200 const char *pinadmin, int make, int dynamic, int refcount,
1201 const struct ast_channel *chan, struct ast_test *test)
1203 struct ast_conference *cnf;
1204 struct dahdi_confinfo dahdic = { 0, };
1206 struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
1207 struct ast_format tmp_fmt;
1209 AST_LIST_LOCK(&confs);
1211 AST_LIST_TRAVERSE(&confs, cnf, list) {
1212 if (!strcmp(confno, cnf->confno))
1216 if (cnf || (!make && !dynamic) || !cap_slin)
1219 ast_format_cap_add(cap_slin, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0));
1220 /* Make a new one */
1221 if (!(cnf = ast_calloc(1, sizeof(*cnf))) ||
1222 !(cnf->usercontainer = ao2_container_alloc(1, NULL, user_no_cmp))) {
1226 ast_mutex_init(&cnf->playlock);
1227 ast_mutex_init(&cnf->listenlock);
1228 cnf->recordthread = AST_PTHREADT_NULL;
1229 ast_mutex_init(&cnf->recordthreadlock);
1230 cnf->announcethread = AST_PTHREADT_NULL;
1231 ast_mutex_init(&cnf->announcethreadlock);
1232 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
1233 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
1234 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
1235 ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
1237 /* Setup a new dahdi conference */
1239 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1240 cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
1241 if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
1243 /* if we are creating a conference for a unit test, it is not neccesary
1244 * to open a pseudo channel, so, if we fail continue creating
1245 * the conference. */
1246 ast_test_status_update(test, "Unable to open DAHDI pseudo device\n");
1248 ast_log(LOG_WARNING, "Unable to open DAHDI pseudo device\n");
1251 ao2_ref(cnf->usercontainer, -1);
1252 ast_mutex_destroy(&cnf->playlock);
1253 ast_mutex_destroy(&cnf->listenlock);
1254 ast_mutex_destroy(&cnf->recordthreadlock);
1255 ast_mutex_destroy(&cnf->announcethreadlock);
1262 cnf->dahdiconf = dahdic.confno;
1264 /* Setup a new channel for playback of audio files */
1265 cnf->chan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL);
1267 ast_set_read_format_by_id(cnf->chan, AST_FORMAT_SLINEAR);
1268 ast_set_write_format_by_id(cnf->chan, AST_FORMAT_SLINEAR);
1270 dahdic.confno = cnf->dahdiconf;
1271 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1272 if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &dahdic)) {
1274 ast_test_status_update(test, "Error setting conference on pseudo channel\n");
1276 ast_log(LOG_WARNING, "Error setting conference\n");
1278 ast_hangup(cnf->chan);
1281 ao2_ref(cnf->usercontainer, -1);
1282 ast_mutex_destroy(&cnf->playlock);
1283 ast_mutex_destroy(&cnf->listenlock);
1284 ast_mutex_destroy(&cnf->recordthreadlock);
1285 ast_mutex_destroy(&cnf->announcethreadlock);
1292 /* Fill the conference struct */
1293 cnf->start = time(NULL);
1294 cnf->maxusers = 0x7fffffff;
1295 cnf->isdynamic = dynamic ? 1 : 0;
1296 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
1297 AST_LIST_INSERT_HEAD(&confs, cnf, list);
1299 /* Reserve conference number in map */
1300 if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1301 conf_map[confno_int] = 1;
1304 cap_slin = ast_format_cap_destroy(cap_slin);
1306 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
1308 AST_LIST_UNLOCK(&confs);
1313 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
1315 static const char * const cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
1317 int len = strlen(word);
1319 struct ast_conference *cnf = NULL;
1320 struct ast_conf_user *usr = NULL;
1321 char *confno = NULL;
1322 char usrno[50] = "";
1323 char *myline, *ret = NULL;
1325 if (pos == 1) { /* Command */
1326 return ast_cli_complete(word, cmds, state);
1327 } else if (pos == 2) { /* Conference Number */
1328 AST_LIST_LOCK(&confs);
1329 AST_LIST_TRAVERSE(&confs, cnf, list) {
1330 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
1335 ret = ast_strdup(ret); /* dup before releasing the lock */
1336 AST_LIST_UNLOCK(&confs);
1338 } else if (pos == 3) {
1339 /* User Number || Conf Command option*/
1340 if (strstr(line, "mute") || strstr(line, "kick")) {
1341 if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
1342 return ast_strdup("all");
1344 AST_LIST_LOCK(&confs);
1346 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
1347 myline = ast_strdupa(line);
1348 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
1349 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
1353 AST_LIST_TRAVERSE(&confs, cnf, list) {
1354 if (!strcmp(confno, cnf->confno))
1359 struct ao2_iterator user_iter;
1360 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
1362 while((usr = ao2_iterator_next(&user_iter))) {
1363 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
1364 if (!strncasecmp(word, usrno, len) && ++which > state) {
1370 ao2_iterator_destroy(&user_iter);
1371 AST_LIST_UNLOCK(&confs);
1372 return usr ? ast_strdup(usrno) : NULL;
1380 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1382 /* Process the command */
1383 struct ast_conf_user *user;
1384 struct ast_conference *cnf;
1386 int i = 0, total = 0;
1388 struct ast_str *cmdline = NULL;
1389 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
1390 #define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
1394 e->command = "meetme list [concise]";
1396 "Usage: meetme list [concise] <confno> \n"
1397 " List all or a specific conference.\n";
1400 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1403 /* Check for length so no buffer will overflow... */
1404 for (i = 0; i < a->argc; i++) {
1405 if (strlen(a->argv[i]) > 100)
1406 ast_cli(a->fd, "Invalid Arguments.\n");
1409 /* Max confno length */
1410 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1414 if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], "concise"))) {
1415 /* List all the conferences */
1416 int concise = (a->argc == 3 && !strcasecmp(a->argv[2], "concise"));
1418 AST_LIST_LOCK(&confs);
1419 if (AST_LIST_EMPTY(&confs)) {
1421 ast_cli(a->fd, "No active MeetMe conferences.\n");
1423 AST_LIST_UNLOCK(&confs);
1428 ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
1430 AST_LIST_TRAVERSE(&confs, cnf, list) {
1431 if (cnf->markedusers == 0) {
1432 ast_str_set(&cmdline, 0, "N/A ");
1434 ast_str_set(&cmdline, 0, "%4.4d", cnf->markedusers);
1436 hr = (now - cnf->start) / 3600;
1437 min = ((now - cnf->start) % 3600) / 60;
1438 sec = (now - cnf->start) % 60;
1440 ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users, ast_str_buffer(cmdline), hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
1442 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1451 total += cnf->users;
1453 AST_LIST_UNLOCK(&confs);
1455 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1459 } else if (strcmp(a->argv[1], "list") == 0) {
1460 struct ao2_iterator user_iter;
1461 int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
1462 /* List all the users in a conference */
1463 if (AST_LIST_EMPTY(&confs)) {
1465 ast_cli(a->fd, "No active MeetMe conferences.\n");
1470 /* Find the right conference */
1471 AST_LIST_LOCK(&confs);
1472 AST_LIST_TRAVERSE(&confs, cnf, list) {
1473 if (strcmp(cnf->confno, a->argv[2]) == 0) {
1479 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
1480 AST_LIST_UNLOCK(&confs);
1484 /* Show all the users */
1486 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
1487 while((user = ao2_iterator_next(&user_iter))) {
1488 hr = (now - user->jointime) / 3600;
1489 min = ((now - user->jointime) % 3600) / 60;
1490 sec = (now - user->jointime) % 60;
1492 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1494 S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
1495 S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<no name>"),
1497 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "(Admin)" : "",
1498 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "(Listen only)" : "",
1499 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1500 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1501 istalking(user->talking), hr, min, sec);
1503 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1505 S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, ""),
1506 S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, ""),
1508 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "1" : "",
1509 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "1" : "",
1510 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1511 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1512 user->talking, hr, min, sec);
1516 ao2_iterator_destroy(&user_iter);
1518 ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
1520 AST_LIST_UNLOCK(&confs);
1526 return CLI_SHOWUSAGE;
1529 ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
1531 admin_exec(NULL, ast_str_buffer(cmdline));
1538 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1540 /* Process the command */
1541 struct ast_str *cmdline = NULL;
1546 e->command = "meetme {lock|unlock|mute|unmute|kick}";
1548 "Usage: meetme (un)lock|(un)mute|kick <confno> <usernumber>\n"
1549 " Executes a command for the conference or on a conferee\n";
1552 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1556 ast_cli(a->fd, "Invalid Arguments.\n");
1557 /* Check for length so no buffer will overflow... */
1558 for (i = 0; i < a->argc; i++) {
1559 if (strlen(a->argv[i]) > 100)
1560 ast_cli(a->fd, "Invalid Arguments.\n");
1563 /* Max confno length */
1564 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1570 return CLI_SHOWUSAGE;
1573 ast_str_set(&cmdline, 0, "%s", a->argv[2]); /* Argv 2: conference number */
1574 if (strstr(a->argv[1], "lock")) {
1575 if (strcmp(a->argv[1], "lock") == 0) {
1577 ast_str_append(&cmdline, 0, ",L");
1580 ast_str_append(&cmdline, 0, ",l");
1582 } else if (strstr(a->argv[1], "mute")) {
1585 return CLI_SHOWUSAGE;
1587 if (strcmp(a->argv[1], "mute") == 0) {
1589 if (strcmp(a->argv[3], "all") == 0) {
1590 ast_str_append(&cmdline, 0, ",N");
1592 ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);
1596 if (strcmp(a->argv[3], "all") == 0) {
1597 ast_str_append(&cmdline, 0, ",n");
1599 ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
1602 } else if (strcmp(a->argv[1], "kick") == 0) {
1605 return CLI_SHOWUSAGE;
1607 if (strcmp(a->argv[3], "all") == 0) {
1609 ast_str_append(&cmdline, 0, ",K");
1611 /* Kick a single user */
1612 ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
1616 return CLI_SHOWUSAGE;
1619 ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
1621 admin_exec(NULL, ast_str_buffer(cmdline));
1627 static const char *sla_hold_str(unsigned int hold_access)
1629 const char *hold = "Unknown";
1631 switch (hold_access) {
1635 case SLA_HOLD_PRIVATE:
1644 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1646 const struct sla_trunk *trunk;
1650 e->command = "sla show trunks";
1652 "Usage: sla show trunks\n"
1653 " This will list all trunks defined in sla.conf\n";
1660 "=============================================================\n"
1661 "=== Configured SLA Trunks ===================================\n"
1662 "=============================================================\n"
1664 AST_RWLIST_RDLOCK(&sla_trunks);
1665 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1666 struct sla_station_ref *station_ref;
1667 char ring_timeout[16] = "(none)";
1668 if (trunk->ring_timeout)
1669 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1670 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1671 "=== Trunk Name: %s\n"
1672 "=== ==> Device: %s\n"
1673 "=== ==> AutoContext: %s\n"
1674 "=== ==> RingTimeout: %s\n"
1675 "=== ==> BargeAllowed: %s\n"
1676 "=== ==> HoldAccess: %s\n"
1677 "=== ==> Stations ...\n",
1678 trunk->name, trunk->device,
1679 S_OR(trunk->autocontext, "(none)"),
1681 trunk->barge_disabled ? "No" : "Yes",
1682 sla_hold_str(trunk->hold_access));
1683 AST_RWLIST_RDLOCK(&sla_stations);
1684 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1685 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
1686 AST_RWLIST_UNLOCK(&sla_stations);
1687 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
1689 AST_RWLIST_UNLOCK(&sla_trunks);
1690 ast_cli(a->fd, "=============================================================\n\n");
1695 static const char *trunkstate2str(enum sla_trunk_state state)
1697 #define S(e) case e: return # e;
1699 S(SLA_TRUNK_STATE_IDLE)
1700 S(SLA_TRUNK_STATE_RINGING)
1701 S(SLA_TRUNK_STATE_UP)
1702 S(SLA_TRUNK_STATE_ONHOLD)
1703 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1705 return "Uknown State";
1709 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1711 const struct sla_station *station;
1715 e->command = "sla show stations";
1717 "Usage: sla show stations\n"
1718 " This will list all stations defined in sla.conf\n";
1725 "=============================================================\n"
1726 "=== Configured SLA Stations =================================\n"
1727 "=============================================================\n"
1729 AST_RWLIST_RDLOCK(&sla_stations);
1730 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1731 struct sla_trunk_ref *trunk_ref;
1732 char ring_timeout[16] = "(none)";
1733 char ring_delay[16] = "(none)";
1734 if (station->ring_timeout) {
1735 snprintf(ring_timeout, sizeof(ring_timeout),
1736 "%u", station->ring_timeout);
1738 if (station->ring_delay) {
1739 snprintf(ring_delay, sizeof(ring_delay),
1740 "%u", station->ring_delay);
1742 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1743 "=== Station Name: %s\n"
1744 "=== ==> Device: %s\n"
1745 "=== ==> AutoContext: %s\n"
1746 "=== ==> RingTimeout: %s\n"
1747 "=== ==> RingDelay: %s\n"
1748 "=== ==> HoldAccess: %s\n"
1749 "=== ==> Trunks ...\n",
1750 station->name, station->device,
1751 S_OR(station->autocontext, "(none)"),
1752 ring_timeout, ring_delay,
1753 sla_hold_str(station->hold_access));
1754 AST_RWLIST_RDLOCK(&sla_trunks);
1755 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1756 if (trunk_ref->ring_timeout) {
1757 snprintf(ring_timeout, sizeof(ring_timeout),
1758 "%u", trunk_ref->ring_timeout);
1760 strcpy(ring_timeout, "(none)");
1761 if (trunk_ref->ring_delay) {
1762 snprintf(ring_delay, sizeof(ring_delay),
1763 "%u", trunk_ref->ring_delay);
1765 strcpy(ring_delay, "(none)");
1766 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
1767 "=== ==> State: %s\n"
1768 "=== ==> RingTimeout: %s\n"
1769 "=== ==> RingDelay: %s\n",
1770 trunk_ref->trunk->name,
1771 trunkstate2str(trunk_ref->state),
1772 ring_timeout, ring_delay);
1774 AST_RWLIST_UNLOCK(&sla_trunks);
1775 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1778 AST_RWLIST_UNLOCK(&sla_stations);
1779 ast_cli(a->fd, "============================================================\n"
1785 static struct ast_cli_entry cli_meetme[] = {
1786 AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
1787 AST_CLI_DEFINE(meetme_show_cmd, "List all or one conference"),
1788 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
1789 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
1792 static void conf_flush(int fd, struct ast_channel *chan)
1796 /* read any frames that may be waiting on the channel
1800 struct ast_frame *f;
1802 /* when no frames are available, this will wait
1803 for 1 millisecond maximum
1805 while (ast_waitfor(chan, 1)) {
1809 else /* channel was hung up or something else happened */
1814 /* flush any data sitting in the pseudo channel */
1815 x = DAHDI_FLUSH_ALL;
1816 if (ioctl(fd, DAHDI_FLUSH, &x))
1817 ast_log(LOG_WARNING, "Error flushing channel\n");
1821 /*! \brief Remove the conference from the list and free it.
1823 We assume that this was called while holding conflock. */
1824 static int conf_free(struct ast_conference *conf)
1827 struct announce_listitem *item;
1829 AST_LIST_REMOVE(&confs, conf, list);
1830 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1832 if (conf->recording == MEETME_RECORD_ACTIVE) {
1833 conf->recording = MEETME_RECORD_TERMINATE;
1834 AST_LIST_UNLOCK(&confs);
1837 AST_LIST_LOCK(&confs);
1838 if (conf->recording == MEETME_RECORD_OFF)
1840 AST_LIST_UNLOCK(&confs);
1844 for (x = 0; x < AST_FRAME_BITS; x++) {
1845 if (conf->transframe[x])
1846 ast_frfree(conf->transframe[x]);
1847 if (conf->transpath[x])
1848 ast_translator_free_path(conf->transpath[x]);
1850 if (conf->announcethread != AST_PTHREADT_NULL) {
1851 ast_mutex_lock(&conf->announcelistlock);
1852 conf->announcethread_stop = 1;
1853 ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
1854 ast_cond_signal(&conf->announcelist_addition);
1855 ast_mutex_unlock(&conf->announcelistlock);
1856 pthread_join(conf->announcethread, NULL);
1858 while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
1859 /* If it's a voicemail greeting file we don't want to remove it */
1861 ast_filedelete(item->namerecloc, NULL);
1865 ast_mutex_destroy(&conf->announcelistlock);
1868 if (conf->origframe)
1869 ast_frfree(conf->origframe);
1871 ast_hangup(conf->lchan);
1873 ast_hangup(conf->chan);
1876 if (conf->recordingfilename) {
1877 ast_free(conf->recordingfilename);
1879 if (conf->usercontainer) {
1880 ao2_ref(conf->usercontainer, -1);
1882 if (conf->recordingformat) {
1883 ast_free(conf->recordingformat);
1885 ast_mutex_destroy(&conf->playlock);
1886 ast_mutex_destroy(&conf->listenlock);
1887 ast_mutex_destroy(&conf->recordthreadlock);
1888 ast_mutex_destroy(&conf->announcethreadlock);
1894 static void conf_queue_dtmf(const struct ast_conference *conf,
1895 const struct ast_conf_user *sender, struct ast_frame *f)
1897 struct ast_conf_user *user;
1898 struct ao2_iterator user_iter;
1900 user_iter = ao2_iterator_init(conf->usercontainer, 0);
1901 while ((user = ao2_iterator_next(&user_iter))) {
1902 if (user == sender) {
1906 if (ast_write(user->chan, f) < 0)
1907 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1910 ao2_iterator_destroy(&user_iter);
1913 static void sla_queue_event_full(enum sla_event_type type,
1914 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1916 struct sla_event *event;
1918 if (sla.thread == AST_PTHREADT_NULL) {
1922 if (!(event = ast_calloc(1, sizeof(*event))))
1926 event->trunk_ref = trunk_ref;
1927 event->station = station;
1930 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1934 ast_mutex_lock(&sla.lock);
1935 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1936 ast_cond_signal(&sla.cond);
1937 ast_mutex_unlock(&sla.lock);
1940 static void sla_queue_event_nolock(enum sla_event_type type)
1942 sla_queue_event_full(type, NULL, NULL, 0);
1945 static void sla_queue_event(enum sla_event_type type)
1947 sla_queue_event_full(type, NULL, NULL, 1);
1950 /*! \brief Queue a SLA event from the conference */
1951 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1952 struct ast_conference *conf)
1954 struct sla_station *station;
1955 struct sla_trunk_ref *trunk_ref = NULL;
1958 trunk_name = ast_strdupa(conf->confno);
1959 strsep(&trunk_name, "_");
1960 if (ast_strlen_zero(trunk_name)) {
1961 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1965 AST_RWLIST_RDLOCK(&sla_stations);
1966 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1967 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1968 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1974 AST_RWLIST_UNLOCK(&sla_stations);
1977 ast_debug(1, "Trunk not found for event!\n");
1981 sla_queue_event_full(type, trunk_ref, station, 1);
1984 /*! \brief Decrement reference counts, as incremented by find_conf() */
1985 static int dispose_conf(struct ast_conference *conf)
1990 AST_LIST_LOCK(&confs);
1991 if (ast_atomic_dec_and_test(&conf->refcount)) {
1992 /* Take the conference room number out of an inuse state */
1993 if ((sscanf(conf->confno, "%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
1994 conf_map[confno_int] = 0;
1999 AST_LIST_UNLOCK(&confs);
2004 static int rt_extend_conf(const char *confno)
2006 char currenttime[32];
2010 struct ast_variable *var, *orig_var;
2019 ast_localtime(&now, &tm, NULL);
2020 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2022 var = ast_load_realtime("meetme", "confno",
2023 confno, "startTime<= ", currenttime,
2024 "endtime>= ", currenttime, NULL);
2028 /* Identify the specific RealTime conference */
2030 if (!strcasecmp(var->name, "bookid")) {
2031 ast_copy_string(bookid, var->value, sizeof(bookid));
2033 if (!strcasecmp(var->name, "endtime")) {
2034 ast_copy_string(endtime, var->value, sizeof(endtime));
2039 ast_variables_destroy(orig_var);
2041 ast_strptime(endtime, DATE_FORMAT, &tm);
2042 now = ast_mktime(&tm, NULL);
2044 now.tv_sec += extendby;
2046 ast_localtime(&now, &tm, NULL);
2047 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2048 strcat(currenttime, "0"); /* Seconds needs to be 00 */
2050 var = ast_load_realtime("meetme", "confno",
2051 confno, "startTime<= ", currenttime,
2052 "endtime>= ", currenttime, NULL);
2054 /* If there is no conflict with extending the conference, update the DB */
2056 ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
2057 ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
2062 ast_variables_destroy(var);
2066 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
2070 ast_channel_lock(chan);
2071 original_moh = ast_strdupa(chan->musicclass);
2072 ast_string_field_set(chan, musicclass, musicclass);
2073 ast_channel_unlock(chan);
2075 ast_moh_start(chan, original_moh, NULL);
2077 ast_channel_lock(chan);
2078 ast_string_field_set(chan, musicclass, original_moh);
2079 ast_channel_unlock(chan);
2082 static const char *get_announce_filename(enum announcetypes type)
2086 return "conf-hasleft";
2089 return "conf-hasjoin";
2096 static void *announce_thread(void *data)
2098 struct announce_listitem *current;
2099 struct ast_conference *conf = data;
2101 char filename[PATH_MAX] = "";
2102 AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
2103 AST_LIST_HEAD_INIT_NOLOCK(&local_list);
2105 while (!conf->announcethread_stop) {
2106 ast_mutex_lock(&conf->announcelistlock);
2107 if (conf->announcethread_stop) {
2108 ast_mutex_unlock(&conf->announcelistlock);
2111 if (AST_LIST_EMPTY(&conf->announcelist))
2112 ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
2114 AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
2115 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2117 ast_mutex_unlock(&conf->announcelistlock);
2118 if (conf->announcethread_stop) {
2122 for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
2123 ast_debug(1, "About to play %s\n", current->namerecloc);
2124 if (!ast_fileexists(current->namerecloc, NULL, NULL))
2126 if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
2127 if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
2128 res = ast_waitstream(current->confchan, "");
2130 ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
2131 if (!ast_streamfile(current->confchan, filename, current->language))
2132 ast_waitstream(current->confchan, "");
2135 if (current->announcetype == CONF_HASLEFT && current->announcetype && !current->vmrec) {
2136 /* only remove it if it isn't a VM recording file */
2137 ast_filedelete(current->namerecloc, NULL);
2142 /* thread marked to stop, clean up */
2143 while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
2144 /* only delete if it's a vm rec */
2145 if (!current->vmrec) {
2146 ast_filedelete(current->namerecloc, NULL);
2148 ao2_ref(current, -1);
2153 static int can_write(struct ast_channel *chan, struct ast_flags64 *confflags)
2155 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
2159 return (chan->_state == AST_STATE_UP);
2162 static void send_talking_event(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking)
2164 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalking",
2170 chan->name, chan->uniqueid, conf->confno, user->user_no, talking ? "on" : "off");
2173 static void set_user_talking(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor)
2175 int last_talking = user->talking;
2176 if (last_talking == talking)
2179 user->talking = talking;
2182 /* Check if talking state changed. Take care of -1 which means unmonitored */
2183 int was_talking = (last_talking > 0);
2184 int now_talking = (talking > 0);
2185 if (was_talking != now_talking) {
2186 send_talking_event(chan, conf, user, now_talking);
2191 static int user_set_kickme_cb(void *obj, void *check_admin_arg, int flags)
2193 struct ast_conf_user *user = obj;
2194 /* actual pointer contents of check_admin_arg is irrelevant */
2196 if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
2197 user->adminflags |= ADMINFLAG_KICKME;
2202 static int user_set_unmuted_cb(void *obj, void *check_admin_arg, int flags)
2204 struct ast_conf_user *user = obj;
2205 /* actual pointer contents of check_admin_arg is irrelevant */
2207 if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
2208 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
2213 static int user_set_muted_cb(void *obj, void *check_admin_arg, int flags)
2215 struct ast_conf_user *user = obj;
2216 /* actual pointer contents of check_admin_arg is irrelevant */
2218 if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
2219 user->adminflags |= ADMINFLAG_MUTED;
2224 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struct ast_flags64 *confflags, char *optargs[])
2226 struct ast_conf_user *user = NULL;
2228 struct dahdi_confinfo dahdic, dahdic_empty;
2229 struct ast_frame *f;
2230 struct ast_channel *c;
2231 struct ast_frame fr;
2238 int musiconhold = 0, mohtempstopped = 0;
2241 int currentmarked = 0;
2244 int menu_active = 0;
2245 int menu8_active = 0;
2246 int talkreq_manager = 0;
2247 int using_pseudo = 0;
2252 int announcement_played = 0;
2254 struct ast_dsp *dsp = NULL;
2255 struct ast_app *agi_app;
2256 char *agifile, *mod_speex;
2257 const char *agifiledefault = "conf-background.agi", *tmpvar;
2258 char meetmesecs[30] = "";
2259 char exitcontext[AST_MAX_CONTEXT] = "";
2260 char recordingtmp[AST_MAX_EXTENSION] = "";
2261 char members[10] = "";
2262 int dtmf, opt_waitmarked_timeout = 0;
2264 struct dahdi_bufferinfo bi;
2265 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
2266 char *buf = __buf + AST_FRIENDLY_OFFSET;
2267 char *exitkeys = NULL;
2268 unsigned int calldurationlimit = 0;
2270 long play_warning = 0;
2271 long warning_freq = 0;
2272 const char *warning_sound = NULL;
2273 const char *end_sound = NULL;
2275 long time_left_ms = 0;
2276 struct timeval nexteventts = { 0, };
2278 int setusercount = 0;
2279 int confsilence = 0, totalsilence = 0;
2280 char *mailbox, *context;
2281 struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
2282 struct ast_format tmpfmt;
2285 goto conf_run_cleanup;
2287 ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
2289 if (!(user = ao2_alloc(sizeof(*user), NULL))) {
2290 goto conf_run_cleanup;
2293 /* Possible timeout waiting for marked user */
2294 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
2295 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
2296 (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
2297 (opt_waitmarked_timeout > 0)) {
2298 timeout = time(NULL) + opt_waitmarked_timeout;
2301 if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
2302 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
2303 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
2306 if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
2307 char *limit_str, *warning_str, *warnfreq_str;
2310 parse = optargs[OPT_ARG_DURATION_LIMIT];
2311 limit_str = strsep(&parse, ":");
2312 warning_str = strsep(&parse, ":");
2313 warnfreq_str = parse;
2315 timelimit = atol(limit_str);
2317 play_warning = atol(warning_str);
2319 warning_freq = atol(warnfreq_str);
2322 timelimit = play_warning = warning_freq = 0;
2323 warning_sound = NULL;
2324 } else if (play_warning > timelimit) {
2325 if (!warning_freq) {
2328 while (play_warning > timelimit)
2329 play_warning -= warning_freq;
2330 if (play_warning < 1)
2331 play_warning = warning_freq = 0;
2335 ast_verb(3, "Setting conference duration limit to: %ldms.\n", timelimit);
2337 ast_verb(3, "Setting warning time to %ldms from the conference duration limit.\n", play_warning);
2340 ast_verb(3, "Setting warning frequency to %ldms.\n", warning_freq);
2343 ast_channel_lock(chan);
2344 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
2345 var = ast_strdupa(var);
2347 ast_channel_unlock(chan);
2349 warning_sound = var ? var : "timeleft";
2351 ast_channel_lock(chan);
2352 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
2353 var = ast_strdupa(var);
2355 ast_channel_unlock(chan);
2357 end_sound = var ? var : NULL;
2359 /* undo effect of S(x) in case they are both used */
2360 calldurationlimit = 0;
2361 /* more efficient do it like S(x) does since no advanced opts */
2362 if (!play_warning && !end_sound && timelimit) {
2363 calldurationlimit = timelimit / 1000;
2364 timelimit = play_warning = warning_freq = 0;
2366 ast_debug(2, "Limit Data for this call:\n");
2367 ast_debug(2, "- timelimit = %ld\n", timelimit);
2368 ast_debug(2, "- play_warning = %ld\n", play_warning);
2369 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
2370 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
2371 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
2376 if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
2377 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
2378 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
2380 exitkeys = ast_strdupa("#"); /* Default */
2383 if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
2384 if (!conf->recordingfilename) {
2386 ast_channel_lock(chan);
2387 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
2388 conf->recordingfilename = ast_strdup(var);
2390 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
2391 conf->recordingformat = ast_strdup(var);
2393 ast_channel_unlock(chan);
2394 if (!conf->recordingfilename) {
2395 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
2396 conf->recordingfilename = ast_strdup(recordingtmp);
2398 if (!conf->recordingformat) {
2399 conf->recordingformat = ast_strdup("wav");
2401 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
2402 conf->confno, conf->recordingfilename, conf->recordingformat);
2406 ast_mutex_lock(&conf->recordthreadlock);
2407 if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
2408 ((conf->lchan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL)))) {
2409 ast_set_read_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
2410 ast_set_write_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
2412 dahdic.confno = conf->dahdiconf;
2413 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
2414 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
2415 ast_log(LOG_WARNING, "Error starting listen channel\n");
2416 ast_hangup(conf->lchan);
2419 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
2422 ast_mutex_unlock(&conf->recordthreadlock);
2424 ast_mutex_lock(&conf->announcethreadlock);
2425 if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
2426 ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC)) {
2427 ast_mutex_init(&conf->announcelistlock);
2428 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2429 ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
2431 ast_mutex_unlock(&conf->announcethreadlock);
2433 time(&user->jointime);
2435 user->timelimit = timelimit;
2436 user->play_warning = play_warning;
2437 user->warning_freq = warning_freq;
2438 user->warning_sound = warning_sound;
2439 user->end_sound = end_sound;
2441 if (calldurationlimit > 0) {
2442 time(&user->kicktime);
2443 user->kicktime = user->kicktime + calldurationlimit;
2446 if (ast_tvzero(user->start_time))
2447 user->start_time = ast_tvnow();
2448 time_left_ms = user->timelimit;
2450 if (user->timelimit) {
2451 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2452 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
2455 if (conf->locked && (!ast_test_flag64(confflags, CONFFLAG_ADMIN))) {
2456 /* Sorry, but this conference is locked! */
2457 if (!ast_streamfile(chan, "conf-locked", chan->language))
2458 ast_waitstream(chan, "");
2462 ast_mutex_lock(&conf->playlock);
2464 if (rt_schedule && conf->maxusers) {
2465 if (conf->users >= conf->maxusers) {
2466 /* Sorry, but this confernce has reached the participant limit! */
2467 if (!ast_streamfile(chan, "conf-full", chan->language))
2468 ast_waitstream(chan, "");
2469 ast_mutex_unlock(&conf->playlock);
2474 ao2_lock(conf->usercontainer);
2475 ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &user->user_no);
2477 ao2_link(conf->usercontainer, user);
2478 ao2_unlock(conf->usercontainer);
2481 user->userflags = *confflags;
2482 user->adminflags = ast_test_flag64(confflags, CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
2483 user->adminflags |= (conf->gmuted) ? ADMINFLAG_MUTED : 0;
2486 ast_mutex_unlock(&conf->playlock);
2488 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC))) {
2489 char destdir[PATH_MAX];
2491 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
2493 if (ast_mkdir(destdir, 0777) != 0) {
2494 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
2498 if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
2499 context = ast_strdupa(optargs[OPT_ARG_INTROUSER_VMREC]);
2500 mailbox = strsep(&context, "@");
2502 if (ast_strlen_zero(mailbox)) {
2503 /* invalid input, clear the v flag*/
2504 ast_clear_flag64(confflags,CONFFLAG_INTROUSER_VMREC);
2505 ast_log(LOG_WARNING,"You must specify a mailbox in the v() option\n");
2507 if (ast_strlen_zero(context)) {
2508 context = "default";
2510 /* if there is no mailbox we don't need to do this logic */
2511 snprintf(user->namerecloc, sizeof(user->namerecloc),
2512 "%s/voicemail/%s/%s/greet",ast_config_AST_SPOOL_DIR,context,mailbox);
2514 /* if the greeting doesn't exist then use the temp file method instead, clear flag v */
2515 if (!ast_fileexists(user->namerecloc, NULL, NULL)){
2516 snprintf(user->namerecloc, sizeof(user->namerecloc),
2517 "%s/meetme-username-%s-%d", destdir,
2518 conf->confno, user->user_no);
2519 ast_clear_flag64(confflags, CONFFLAG_INTROUSER_VMREC);
2523 snprintf(user->namerecloc, sizeof(user->namerecloc),
2524 "%s/meetme-username-%s-%d", destdir,
2525 conf->confno, user->user_no);
2529 if (ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) && !ast_fileexists(user->namerecloc, NULL, NULL))
2530 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
2531 else if (ast_test_flag64(confflags, CONFFLAG_INTROUSER) && !ast_fileexists(user->namerecloc, NULL, NULL))
2532 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
2538 ast_mutex_lock(&conf->playlock);
2540 if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER))
2541 conf->markedusers++;
2543 if (rt_log_members) {
2545 snprintf(members, sizeof(members), "%d", conf->users);
2546 ast_realtime_require_field("meetme",
2547 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
2548 "members", RQ_UINTEGER1, strlen(members),
2550 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2554 /* This device changed state now - if this is the first user */
2555 if (conf->users == 1)
2556 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
2558 ast_mutex_unlock(&conf->playlock);
2560 /* return the unique ID of the conference */
2561 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
2563 if (ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT)) {
2564 ast_channel_lock(chan);
2565 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
2566 ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
2567 } else if (!ast_strlen_zero(chan->macrocontext)) {
2568 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
2570 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
2572 ast_channel_unlock(chan);
2575 /* Play an arbitrary intro message */
2576 if (ast_test_flag64(confflags, CONFFLAG_INTROMSG) &&
2577 !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
2578 if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], chan->language)) {
2579 ast_waitstream(chan, "");
2583 if (!ast_test_flag64(confflags, (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
2584 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED))
2585 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
2586 ast_waitstream(chan, "");
2587 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && conf->markedusers == 0)
2588 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
2589 ast_waitstream(chan, "");
2592 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_ANNOUNCEUSERCOUNT) &&
2594 int keepplaying = 1;
2596 if (conf->users == 2) {
2597 if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
2598 res = ast_waitstream(chan, AST_DIGIT_ANY);
2599 ast_stopstream(chan);
2606 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
2607 res = ast_waitstream(chan, AST_DIGIT_ANY);
2608 ast_stopstream(chan);
2615 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2621 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
2622 res = ast_waitstream(chan, AST_DIGIT_ANY);
2623 ast_stopstream(chan);
2632 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
2633 /* We're leaving this alone until the state gets changed to up */
2634 ast_indicate(chan, -1);
2637 if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
2638 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
2642 if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
2643 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
2647 /* Reduce background noise from each participant */
2648 if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
2649 ast_free(mod_speex);
2650 ast_func_write(chan, "DENOISE(rx)", "on");
2653 retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
2654 user->dahdichannel = !retrydahdi;
2657 origfd = chan->fds[0];
2659 /* open pseudo in non-blocking mode */
2660 fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
2662 ast_log(LOG_WARNING, "Unable to open DAHDI pseudo channel: %s\n", strerror(errno));
2666 /* Setup buffering information */
2667 memset(&bi, 0, sizeof(bi));
2668 bi.bufsize = CONF_SIZE / 2;
2669 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
2670 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
2671 bi.numbufs = audio_buffers;
2672 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
2673 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
2678 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
2679 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
2685 /* XXX Make sure we're not running on a pseudo channel XXX */
2689 memset(&dahdic, 0, sizeof(dahdic));
2690 memset(&dahdic_empty, 0, sizeof(dahdic_empty));
2691 /* Check to see if we're in a conference... */
2693 if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
2694 ast_log(LOG_WARNING, "Error getting conference\n");
2698 if (dahdic.confmode) {
2699 /* Whoa, already in a conference... Retry... */
2701 ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
2706 memset(&dahdic, 0, sizeof(dahdic));
2707 /* Add us to the conference */
2709 dahdic.confno = conf->dahdiconf;
2711 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
2712 ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) || ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)) && conf->users > 1) {
2713 struct announce_listitem *item;
2714 if (!(item = ao2_alloc(sizeof(*item), NULL)))
2716 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
2717 ast_copy_string(item->language, chan->language, sizeof(item->language));
2718 item->confchan = conf->chan;
2719 item->confusers = conf->users;
2720 if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
2723 item->announcetype = CONF_HASJOIN;
2724 ast_mutex_lock(&conf->announcelistlock);
2725 ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
2726 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
2727 ast_cond_signal(&conf->announcelist_addition);
2728 ast_mutex_unlock(&conf->announcelistlock);
2730 while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
2736 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED && !conf->markedusers))
2737 dahdic.confmode = DAHDI_CONF_CONF;
2738 else if (ast_test_flag64(confflags, CONFFLAG_MONITOR))
2739 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2740 else if (ast_test_flag64(confflags, CONFFLAG_TALKER))
2741 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2743 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2745 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2746 ast_log(LOG_WARNING, "Error setting conference\n");
2750 ast_debug(1, "Placed channel %s in DAHDI conf %d\n", chan->name, conf->dahdiconf);
2753 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeJoin",
2758 "CallerIDnum: %s\r\n"
2759 "CallerIDname: %s\r\n",
2760 chan->name, chan->uniqueid, conf->confno,
2762 S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
2763 S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<unknown>")
2768 if (!firstpass && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
2769 !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
2771 if (!ast_test_flag64(confflags, CONFFLAG_QUIET))
2772 if (!ast_test_flag64(confflags, CONFFLAG_WAITMARKED) || (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
2773 (conf->markedusers >= 1))) {
2774 conf_play(chan, conf, ENTER);
2778 conf_flush(fd, chan);
2783 if (!(dsp = ast_dsp_new())) {
2784 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
2788 if (ast_test_flag64(confflags, CONFFLAG_AGI)) {
2789 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
2790 or use default filename of conf-background.agi */
2792 ast_channel_lock(chan);
2793 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
2794 agifile = ast_strdupa(tmpvar);
2796 agifile = ast_strdupa(agifiledefault);
2798 ast_channel_unlock(chan);
2800 if (user->dahdichannel) {
2801 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
2803 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2805 /* Find a pointer to the agi app and execute the script */
2806 agi_app = pbx_findapp("agi");
2808 ret = pbx_exec(chan, agi_app, agifile);
2810 ast_log(LOG_WARNING, "Could not find application (agi)\n");
2813 if (user->dahdichannel) {
2814 /* Remove CONFMUTE mode on DAHDI channel */
2816 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2819 if (user->dahdichannel && ast_test_flag64(confflags, CONFFLAG_STARMENU)) {
2820 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
2822 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2825 int menu_was_active = 0;
2831 if (rt_schedule && conf->endtime) {
2832 char currenttime[32];
2833 long localendtime = 0;
2836 struct ast_variable *var, *origvar;
2839 if (now.tv_sec % 60 == 0) {
2841 ast_localtime(&now, &tm, NULL);
2842 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2843 var = origvar = ast_load_realtime("meetme", "confno",
2844 conf->confno, "starttime <=", currenttime,
2845 "endtime >=", currenttime, NULL);
2847 for ( ; var; var = var->next) {
2848 if (!strcasecmp(var->name, "endtime")) {
2849 struct ast_tm endtime_tm;
2850 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
2851 tmp = ast_mktime(&endtime_tm, NULL);
2852 localendtime = tmp.tv_sec;
2855 ast_variables_destroy(origvar);
2857 /* A conference can be extended from the
2858 Admin/User menu or by an external source */
2859 if (localendtime > conf->endtime){
2860 conf->endtime = localendtime;
2864 if (conf->endtime && (now.tv_sec >= conf->endtime)) {
2865 ast_verbose("Quitting time...\n");
2869 if (!announcement_played && conf->endalert) {
2870 if (now.tv_sec + conf->endalert >= conf->endtime) {
2871 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
2872 ast_waitstream(chan, "");
2873 ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", chan->language);
2874 if (!ast_streamfile(chan, "minutes", chan->language))
2875 ast_waitstream(chan, "");
2877 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2879 announcement_played = 1;
2884 announcement_played = 0;
2894 if (user->kicktime && (user->kicktime <= now.tv_sec)) {
2895 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
2904 if (user->timelimit) {
2905 int minutes = 0, seconds = 0, remain = 0;
2907 to = ast_tvdiff_ms(nexteventts, now);
2911 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
2912 if (time_left_ms < to) {
2916 if (time_left_ms <= 0) {
2917 if (user->end_sound) {
2918 res = ast_streamfile(chan, user->end_sound, chan->language);
2919 res = ast_waitstream(chan, "");
2921 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
2930 if (time_left_ms >= 5000) {
2932 remain = (time_left_ms + 500) / 1000;
2933 if (remain / 60 >= 1) {
2934 minutes = remain / 60;
2935 seconds = remain % 60;
2940 /* force the time left to round up if appropriate */
2941 if (user->warning_sound && user->play_warning) {
2942 if (!strcmp(user->warning_sound, "timeleft")) {
2944 res = ast_streamfile(chan, "vm-youhave", chan->language);
2945 res = ast_waitstream(chan, "");
2947 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
2948 res = ast_streamfile(chan, "queue-minutes", chan->language);
2949 res = ast_waitstream(chan, "");
2952 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
2953 res = ast_streamfile(chan, "queue-seconds", chan->language);
2954 res = ast_waitstream(chan, "");
2957 res = ast_streamfile(chan, user->warning_sound, chan->language);
2958 res = ast_waitstream(chan, "");
2961 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2965 if (user->warning_freq) {
2966 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
2968 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2974 if (timeout && now.tv_sec >= timeout) {
2975 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
2983 /* if we have just exited from the menu, and the user had a channel-driver
2984 volume adjustment, restore it
2986 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual) {
2987 set_talk_volume(user, user->listen.desired);
2990 menu_was_active = menu_active;
2992 currentmarked = conf->markedusers;
2993 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
2994 ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
2995 ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
2997 if (currentmarked == 1 && conf->users > 1) {
2998 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2999 if (conf->users - 1 == 1) {
3000 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language)) {
3001 ast_waitstream(chan, "");
3004 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language)) {
3005 ast_waitstream(chan, "");
3009 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
3010 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
3011 ast_waitstream(chan, "");
3016 /* Update the struct with the actual confflags */
3017 user->userflags = *confflags;
3019 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
3020 if (currentmarked == 0) {
3021 if (lastmarked != 0) {
3022 if (!ast_test_flag64(confflags, CONFFLAG_QUIET)) {
3023 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language)) {
3024 ast_waitstream(chan, "");
3027 if (ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
3028 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
3033 dahdic.confmode = DAHDI_CONF_CONF;
3034 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3035 ast_log(LOG_WARNING, "Error setting conference\n");
3041 if (!musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
3042 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3045 } else if (currentmarked >= 1 && lastmarked == 0) {
3046 /* Marked user entered, so cancel timeout */
3048 if (ast_test_flag64(confflags, CONFFLAG_MONITOR)) {
3049 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
3050 } else if (ast_test_flag64(confflags, CONFFLAG_TALKER)) {
3051 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
3053 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
3055 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3056 ast_log(LOG_WARNING, "Error setting conference\n");
3060 if (musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
3064 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
3065 !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
3066 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language)) {
3067 ast_waitstream(chan, "");
3069 conf_play(chan, conf, ENTER);
3074 /* trying to add moh for single person conf */
3075 if (ast_test_flag64(confflags, CONFFLAG_MOH) && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
3076 if (conf->users == 1) {
3078 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3089 /* Leave if the last marked user left */
3090 if (currentmarked == 0 && lastmarked != 0 && ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
3091 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
3099 /* Check if my modes have changed */
3101 /* If I should be muted but am still talker, mute me */
3102 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
3103 dahdic.confmode ^= DAHDI_CONF_TALKER;
3104 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3105 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
3110 /* Indicate user is not talking anymore - change him to unmonitored state */
3111 if (ast_test_flag64(confflags, (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER))) {
3112 set_user_talking(chan, conf, user, -1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
3115 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
3121 chan->name, chan->uniqueid, conf->confno, user->user_no);
3124 /* If I should be un-muted but am not talker, un-mute me */
3125 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
3126 dahdic.confmode |= DAHDI_CONF_TALKER;
3127 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3128 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
3133 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
3139 chan->name, chan->uniqueid, conf->confno, user->user_no);
3142 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
3143 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
3144 talkreq_manager = 1;
3146 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
3152 chan->name, chan->uniqueid, conf->confno, user->user_no);
3156 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
3157 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
3158 talkreq_manager = 0;
3159 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
3165 chan->name, chan->uniqueid, conf->confno, user->user_no);
3168 /* If I have been kicked, exit the conference */
3169 if (user->adminflags & ADMINFLAG_KICKME) {