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>
34 <defaultenabled>no</defaultenabled>
35 <support_level>deprecated</support_level>
36 <replacement>app_confbridge</replacement>
41 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
43 #include <dahdi/user.h>
45 #include "asterisk/lock.h"
46 #include "asterisk/file.h"
47 #include "asterisk/channel.h"
48 #include "asterisk/pbx.h"
49 #include "asterisk/module.h"
50 #include "asterisk/config.h"
51 #include "asterisk/app.h"
52 #include "asterisk/dsp.h"
53 #include "asterisk/musiconhold.h"
54 #include "asterisk/manager.h"
55 #include "asterisk/cli.h"
56 #include "asterisk/say.h"
57 #include "asterisk/utils.h"
58 #include "asterisk/translate.h"
59 #include "asterisk/ulaw.h"
60 #include "asterisk/astobj2.h"
61 #include "asterisk/devicestate.h"
62 #include "asterisk/dial.h"
63 #include "asterisk/causes.h"
64 #include "asterisk/paths.h"
65 #include "asterisk/data.h"
66 #include "asterisk/test.h"
72 <application name="MeetMe" language="en_US">
74 MeetMe conference bridge.
77 <parameter name="confno">
78 <para>The conference number</para>
80 <parameter name="options">
83 <para>Set admin mode.</para>
86 <para>Set marked mode.</para>
89 <para>Run AGI script specified in <variable>MEETME_AGI_BACKGROUND</variable>
90 Default: <literal>conf-background.agi</literal>.</para>
91 <note><para>This does not work with non-DAHDI channels in the same
92 conference).</para></note>
95 <para>Announce user(s) count on joining a conference.</para>
98 <para>Continue in dialplan when kicked out of conference.</para>
101 <para>Dynamically add conference.</para>
104 <para>Dynamically add conference, prompting for a PIN.</para>
107 <para>Select an empty conference.</para>
110 <para>Select an empty pinless conference.</para>
113 <para>Pass DTMF through the conference.</para>
116 <argument name="x" required="true">
117 <para>The file to playback</para>
119 <para>Play an intro announcement in conference.</para>
122 <para>Announce user join/leave with review.</para>
125 <para>Announce user join/leave without review.</para>
128 <para>Close the conference if there's only one active participant left at exit.</para>
131 <para>Set listen only mode (Listen only, no talking).</para>
134 <para>Set initially muted.</para>
136 <option name="M" hasparams="optional">
137 <para>Enable music on hold when the conference has a single caller. Optionally,
138 specify a musiconhold class to use. If one is not provided, it will use the
139 channel's currently set music class, or <literal>default</literal>.</para>
140 <argument name="class" required="true" />
143 <para>Set talker optimization - treats talkers who aren't speaking as
144 being muted, meaning (a) No encode is done on transmission and (b)
145 Received audio that is not registered as talking is omitted causing no
146 buildup in background noise.</para>
148 <option name="p" hasparams="optional">
149 <para>Allow user to exit the conference by pressing <literal>#</literal> (default)
150 or any of the defined keys. The key used is set to channel variable
151 <variable>MEETME_EXIT_KEY</variable>.</para>
152 <argument name="keys" required="true" />
154 <para>Option <literal>s</literal> has priority for <literal>*</literal>
155 since it cannot change its activation code.</para>
159 <para>Always prompt for the pin even if it is specified.</para>
162 <para>Quiet mode (don't play enter/leave sounds).</para>
165 <para>Record conference (records as <variable>MEETME_RECORDINGFILE</variable>
166 using format <variable>MEETME_RECORDINGFORMAT</variable>. Default filename is
167 <literal>meetme-conf-rec-${CONFNO}-${UNIQUEID}</literal> and the default format is
171 <para>Present menu (user or admin) when <literal>*</literal> is received
172 (send to menu).</para>
175 <para>Set talk only mode. (Talk only, no listening).</para>
178 <para>Set talker detection (sent to manager interface and meetme list).</para>
180 <option name="v" hasparams="optional">
181 <para>Announce when a user is joining or leaving the conference. Use the voicemail greeting as the announcement.
182 If the i or I options are set, the application will fall back to them if no voicemail greeting can be found.</para>
183 <argument name="mailbox@[context]" required="true">
184 <para>The mailbox and voicemail context to play from. If no context provided, assumed context is default.</para>
187 <option name="w" hasparams="optional">
188 <para>Wait until the marked user enters the conference.</para>
189 <argument name="secs" required="true" />
192 <para>Close the conference when last marked user exits</para>
195 <para>Allow user to exit the conference by entering a valid single digit
196 extension <variable>MEETME_EXIT_CONTEXT</variable> or the current context
197 if that variable is not defined.</para>
199 <para>Option <literal>s</literal> has priority for <literal>*</literal>
200 since it cannot change its activation code.</para>
204 <para>Do not play message when first person enters</para>
207 <para>Kick the user <replaceable>x</replaceable> seconds <emphasis>after</emphasis> he entered into
208 the conference.</para>
209 <argument name="x" required="true" />
211 <option name="L" argsep=":">
212 <para>Limit the conference to <replaceable>x</replaceable> ms. Play a warning when
213 <replaceable>y</replaceable> ms are left. Repeat the warning every <replaceable>z</replaceable> ms.
214 The following special variables can be used with this option:</para>
216 <variable name="CONF_LIMIT_TIMEOUT_FILE">
217 <para>File to play when time is up.</para>
219 <variable name="CONF_LIMIT_WARNING_FILE">
220 <para>File to play as warning if <replaceable>y</replaceable> is defined. The
221 default is to say the time remaining.</para>
224 <argument name="x" />
225 <argument name="y" />
226 <argument name="z" />
230 <parameter name="pin" />
233 <para>Enters the user into a specified MeetMe conference. If the <replaceable>confno</replaceable>
234 is omitted, the user will be prompted to enter one. User can exit the conference by hangup, or
235 if the <literal>p</literal> option is specified, by pressing <literal>#</literal>.</para>
236 <note><para>The DAHDI kernel modules and a functional DAHDI timing source (see dahdi_test)
237 must be present for conferencing to operate properly. In addition, the chan_dahdi channel driver
238 must be loaded for the <literal>i</literal> and <literal>r</literal> options to operate at
242 <ref type="application">MeetMeCount</ref>
243 <ref type="application">MeetMeAdmin</ref>
244 <ref type="application">MeetMeChannelAdmin</ref>
247 <application name="MeetMeCount" language="en_US">
249 MeetMe participant count.
252 <parameter name="confno" required="true">
253 <para>Conference number.</para>
255 <parameter name="var" />
258 <para>Plays back the number of users in the specified MeetMe conference.
259 If <replaceable>var</replaceable> is specified, playback will be skipped and the value
260 will be returned in the variable. Upon application completion, MeetMeCount will hangup
261 the channel, unless priority <literal>n+1</literal> exists, in which case priority progress will
265 <ref type="application">MeetMe</ref>
268 <application name="MeetMeAdmin" language="en_US">
270 MeetMe conference administration.
273 <parameter name="confno" required="true" />
274 <parameter name="command" required="true">
277 <para>Eject last user that joined.</para>
280 <para>Extend conference end time, if scheduled.</para>
283 <para>Kick one user out of conference.</para>
286 <para>Kick all users out of conference.</para>
289 <para>Unlock conference.</para>
292 <para>Lock conference.</para>
295 <para>Unmute one user.</para>
298 <para>Mute one user.</para>
301 <para>Unmute all users in the conference.</para>
304 <para>Mute all non-admin users in the conference.</para>
307 <para>Reset one user's volume settings.</para>
310 <para>Reset all users volume settings.</para>
313 <para>Lower entire conference speaking volume.</para>
316 <para>Raise entire conference speaking volume.</para>
319 <para>Lower one user's talk volume.</para>
322 <para>Raise one user's talk volume.</para>
325 <para>Lower one user's listen volume.</para>
328 <para>Raise one user's listen volume.</para>
331 <para>Lower entire conference listening volume.</para>
334 <para>Raise entire conference listening volume.</para>
338 <parameter name="user" />
341 <para>Run admin <replaceable>command</replaceable> for conference <replaceable>confno</replaceable>.</para>
342 <para>Will additionally set the variable <variable>MEETMEADMINSTATUS</variable> with one of
343 the following values:</para>
345 <variable name="MEETMEADMINSTATUS">
346 <value name="NOPARSE">
349 <value name="NOTFOUND">
350 User specified was not found.
352 <value name="FAILED">
353 Another failure occurred.
356 The operation was completed successfully.
362 <ref type="application">MeetMe</ref>
365 <application name="MeetMeChannelAdmin" language="en_US">
367 MeetMe conference Administration (channel specific).
370 <parameter name="channel" required="true" />
371 <parameter name="command" required="true">
374 <para>Kick the specified user out of the conference he is in.</para>
377 <para>Unmute the specified user.</para>
380 <para>Mute the specified user.</para>
386 <para>Run admin <replaceable>command</replaceable> for a specific
387 <replaceable>channel</replaceable> in any conference.</para>
390 <application name="SLAStation" language="en_US">
392 Shared Line Appearance Station.
395 <parameter name="station" required="true">
396 <para>Station name</para>
400 <para>This application should be executed by an SLA station. The argument depends
401 on how the call was initiated. If the phone was just taken off hook, then the argument
402 <replaceable>station</replaceable> should be just the station name. If the call was
403 initiated by pressing a line key, then the station name should be preceded by an underscore
404 and the trunk name associated with that line button.</para>
405 <para>For example: <literal>station1_line1</literal></para>
406 <para>On exit, this application will set the variable <variable>SLASTATION_STATUS</variable> to
407 one of the following values:</para>
409 <variable name="SLASTATION_STATUS">
410 <value name="FAILURE" />
411 <value name="CONGESTION" />
412 <value name="SUCCESS" />
417 <application name="SLATrunk" language="en_US">
419 Shared Line Appearance Trunk.
422 <parameter name="trunk" required="true">
423 <para>Trunk name</para>
425 <parameter name="options">
427 <option name="M" hasparams="optional">
428 <para>Play back the specified MOH <replaceable>class</replaceable>
429 instead of ringing</para>
430 <argument name="class" required="true" />
436 <para>This application should be executed by an SLA trunk on an inbound call. The channel calling
437 this application should correspond to the SLA trunk with the name <replaceable>trunk</replaceable>
438 that is being passed as an argument.</para>
439 <para>On exit, this application will set the variable <variable>SLATRUNK_STATUS</variable> to
440 one of the following values:</para>
442 <variable name="SLATRUNK_STATUS">
443 <value name="FAILURE" />
444 <value name="SUCCESS" />
445 <value name="UNANSWERED" />
446 <value name="RINGTIMEOUT" />
451 <function name="MEETME_INFO" language="en_US">
453 Query a given conference of various properties.
456 <parameter name="keyword" required="true">
457 <para>Options:</para>
460 <para>Boolean of whether the corresponding conference is locked.</para>
462 <enum name="parties">
463 <para>Number of parties in a given conference</para>
465 <enum name="activity">
466 <para>Duration of conference in seconds.</para>
468 <enum name="dynamic">
469 <para>Boolean of whether the corresponding conference is dynamic.</para>
473 <parameter name="confno" required="true">
474 <para>Conference number to retrieve information from.</para>
479 <ref type="application">MeetMe</ref>
480 <ref type="application">MeetMeCount</ref>
481 <ref type="application">MeetMeAdmin</ref>
482 <ref type="application">MeetMeChannelAdmin</ref>
485 <manager name="MeetmeMute" language="en_US">
490 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
491 <parameter name="Meetme" required="true" />
492 <parameter name="Usernum" required="true" />
497 <manager name="MeetmeUnmute" language="en_US">
499 Unmute a Meetme user.
502 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
503 <parameter name="Meetme" required="true" />
504 <parameter name="Usernum" required="true" />
509 <manager name="MeetmeList" language="en_US">
511 List participants in a conference.
514 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
515 <parameter name="Conference" required="false">
516 <para>Conference number.</para>
520 <para>Lists all users in a particular MeetMe conference.
521 MeetmeList will follow as separate events, followed by a final event called
522 MeetmeListComplete.</para>
525 <manager name="MeetmeListRooms" language="en_US">
527 List active conferences.
530 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
533 <para>Lists data about all active conferences.
534 MeetmeListRooms will follow as separate events, followed by a final event called
535 MeetmeListRoomsComplete.</para>
540 #define CONFIG_FILE_NAME "meetme.conf"
541 #define SLA_CONFIG_FILE "sla.conf"
543 /*! each buffer is 20ms, so this is 640ms total */
544 #define DEFAULT_AUDIO_BUFFERS 32
546 /*! String format for scheduled conferences */
547 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
550 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
551 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
552 ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */
553 /*! User has requested to speak */
554 ADMINFLAG_T_REQUEST = (1 << 4),
555 ADMINFLAG_HANGUP = (1 << 5), /*!< User will be leaving the conference */
558 #define MEETME_DELAYDETECTTALK 300
559 #define MEETME_DELAYDETECTENDTALK 1000
561 #define AST_FRAME_BITS 32
568 enum entrance_sound {
573 enum recording_state {
575 MEETME_RECORD_STARTED,
576 MEETME_RECORD_ACTIVE,
577 MEETME_RECORD_TERMINATE
580 #define CONF_SIZE 320
583 /*! user has admin access on the conference */
584 CONFFLAG_ADMIN = (1 << 0),
585 /*! If set the user can only receive audio from the conference */
586 CONFFLAG_MONITOR = (1 << 1),
587 /*! If set asterisk will exit conference when key defined in p() option is pressed */
588 CONFFLAG_KEYEXIT = (1 << 2),
589 /*! If set asterisk will provide a menu to the user when '*' is pressed */
590 CONFFLAG_STARMENU = (1 << 3),
591 /*! If set the use can only send audio to the conference */
592 CONFFLAG_TALKER = (1 << 4),
593 /*! If set there will be no enter or leave sounds */
594 CONFFLAG_QUIET = (1 << 5),
595 /*! If set, when user joins the conference, they will be told the number
596 * of users that are already in */
597 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
598 /*! Set to run AGI Script in Background */
599 CONFFLAG_AGI = (1 << 7),
600 /*! Set to have music on hold when user is alone in conference */
601 CONFFLAG_MOH = (1 << 8),
602 /*! If set the MeetMe will return if all marked with this flag left */
603 CONFFLAG_MARKEDEXIT = (1 << 9),
604 /*! If set, the MeetMe will wait until a marked user enters */
605 CONFFLAG_WAITMARKED = (1 << 10),
606 /*! If set, the MeetMe will exit to the specified context */
607 CONFFLAG_EXIT_CONTEXT = (1 << 11),
608 /*! If set, the user will be marked */
609 CONFFLAG_MARKEDUSER = (1 << 12),
610 /*! If set, user will be ask record name on entry of conference */
611 CONFFLAG_INTROUSER = (1 << 13),
612 /*! If set, the MeetMe will be recorded */
613 CONFFLAG_RECORDCONF = (1<< 14),
614 /*! If set, the user will be monitored if the user is talking or not */
615 CONFFLAG_MONITORTALKER = (1 << 15),
616 CONFFLAG_DYNAMIC = (1 << 16),
617 CONFFLAG_DYNAMICPIN = (1 << 17),
618 CONFFLAG_EMPTY = (1 << 18),
619 CONFFLAG_EMPTYNOPIN = (1 << 19),
620 CONFFLAG_ALWAYSPROMPT = (1 << 20),
621 /*! If set, treat talking users as muted users */
622 CONFFLAG_OPTIMIZETALKER = (1 << 21),
623 /*! If set, won't speak the extra prompt when the first person
624 * enters the conference */
625 CONFFLAG_NOONLYPERSON = (1 << 22),
626 /*! If set, user will be asked to record name on entry of conference
628 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
629 /*! If set, the user will be initially self-muted */
630 CONFFLAG_STARTMUTED = (1 << 24),
631 /*! Pass DTMF through the conference */
632 CONFFLAG_PASS_DTMF = (1 << 25),
633 CONFFLAG_SLA_STATION = (1 << 26),
634 CONFFLAG_SLA_TRUNK = (1 << 27),
635 /*! If set, the user should continue in the dialplan if kicked out */
636 CONFFLAG_KICK_CONTINUE = (1 << 28),
637 CONFFLAG_DURATION_STOP = (1 << 29),
638 CONFFLAG_DURATION_LIMIT = (1 << 30),
641 /* These flags are defined separately because we ran out of bits that an enum can be used to represent.
642 If you add new flags, be sure to do it in the same way that these are. */
643 /*! Do not write any audio to this channel until the state is up. */
644 #define CONFFLAG_NO_AUDIO_UNTIL_UP (1ULL << 31)
645 #define CONFFLAG_INTROMSG (1ULL << 32) /*!< If set play an intro announcement at start of conference */
646 #define CONFFLAG_INTROUSER_VMREC (1ULL << 33)
647 /*! If there's only one person left in a conference when someone leaves, kill the conference */
648 #define CONFFLAG_KILL_LAST_MAN_STANDING ((uint64_t)1 << 34)
651 OPT_ARG_WAITMARKED = 0,
652 OPT_ARG_EXITKEYS = 1,
653 OPT_ARG_DURATION_STOP = 2,
654 OPT_ARG_DURATION_LIMIT = 3,
655 OPT_ARG_MOH_CLASS = 4,
656 OPT_ARG_INTROMSG = 5,
657 OPT_ARG_INTROUSER_VMREC = 6,
658 OPT_ARG_ARRAY_SIZE = 7,
661 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
662 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
663 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
664 AST_APP_OPTION('b', CONFFLAG_AGI ),
665 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
666 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
667 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
668 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
669 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
670 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
671 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
672 AST_APP_OPTION_ARG('G', CONFFLAG_INTROMSG, OPT_ARG_INTROMSG ),
673 AST_APP_OPTION_ARG('v', CONFFLAG_INTROUSER_VMREC , OPT_ARG_INTROUSER_VMREC),
674 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
675 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
676 AST_APP_OPTION('k', CONFFLAG_KILL_LAST_MAN_STANDING ),
677 AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
678 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
679 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
680 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
681 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
682 AST_APP_OPTION('q', CONFFLAG_QUIET ),
683 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
684 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
685 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
686 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
687 AST_APP_OPTION('t', CONFFLAG_TALKER ),
688 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
689 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
690 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
691 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
692 AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
693 AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
696 static const char * const app = "MeetMe";
697 static const char * const app2 = "MeetMeCount";
698 static const char * const app3 = "MeetMeAdmin";
699 static const char * const app4 = "MeetMeChannelAdmin";
700 static const char * const slastation_app = "SLAStation";
701 static const char * const slatrunk_app = "SLATrunk";
703 /* Lookup RealTime conferences based on confno and current time */
704 static int rt_schedule;
705 static int fuzzystart;
706 static int earlyalert;
710 /*! Log participant count to the RealTime backend */
711 static int rt_log_members;
713 #define MAX_CONFNUM 80
715 #define OPTIONS_LEN 100
717 /* Enough space for "<conference #>,<pin>,<admin pin>" followed by a 0 byte. */
718 #define MAX_SETTINGS (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)
725 struct announce_listitem {
726 AST_LIST_ENTRY(announce_listitem) entry;
727 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
728 char language[MAX_LANGUAGE];
729 struct ast_channel *confchan;
732 enum announcetypes announcetype;
735 /*! \brief The MeetMe Conference object */
736 struct ast_conference {
737 ast_mutex_t playlock; /*!< Conference specific lock (players) */
738 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
739 char confno[MAX_CONFNUM]; /*!< Conference */
740 struct ast_channel *chan; /*!< Announcements channel */
741 struct ast_channel *lchan; /*!< Listen/Record channel */
742 int fd; /*!< Announcements fd */
743 int dahdiconf; /*!< DAHDI Conf # */
744 int users; /*!< Number of active users */
745 int markedusers; /*!< Number of marked users */
746 int maxusers; /*!< Participant limit if scheduled */
747 int endalert; /*!< When to play conf ending message */
748 time_t start; /*!< Start time (s) */
749 int refcount; /*!< reference count of usage */
750 enum recording_state recording:2; /*!< recording status */
751 unsigned int isdynamic:1; /*!< Created on the fly? */
752 unsigned int locked:1; /*!< Is the conference locked? */
753 unsigned int gmuted:1; /*!< Is the conference globally muted? (all non-admins) */
754 pthread_t recordthread; /*!< thread for recording */
755 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
756 pthread_attr_t attr; /*!< thread attribute */
757 char *recordingfilename; /*!< Filename to record the Conference into */
758 char *recordingformat; /*!< Format to record the Conference in */
759 char pin[MAX_PIN]; /*!< If protected by a PIN */
760 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
762 long endtime; /*!< When to end the conf if scheduled */
763 const char *useropts; /*!< RealTime user flags */
764 const char *adminopts; /*!< RealTime moderator flags */
765 const char *bookid; /*!< RealTime conference id */
766 struct ast_frame *transframe[32];
767 struct ast_frame *origframe;
768 struct ast_trans_pvt *transpath[32];
769 struct ao2_container *usercontainer;
770 AST_LIST_ENTRY(ast_conference) list;
771 /* announce_thread related data */
772 pthread_t announcethread;
773 ast_mutex_t announcethreadlock;
774 unsigned int announcethread_stop:1;
775 ast_cond_t announcelist_addition;
776 AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
777 ast_mutex_t announcelistlock;
780 static AST_LIST_HEAD_STATIC(confs, ast_conference);
782 static unsigned int conf_map[1024] = {0, };
785 int desired; /*!< Desired volume adjustment */
786 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
789 /*! \brief The MeetMe User object */
790 struct ast_conf_user {
791 int user_no; /*!< User Number */
792 struct ast_flags64 userflags; /*!< Flags as set in the conference */
793 int adminflags; /*!< Flags set by the Admin */
794 struct ast_channel *chan; /*!< Connected channel */
795 int talking; /*!< Is user talking */
796 int dahdichannel; /*!< Is a DAHDI channel */
797 char usrvalue[50]; /*!< Custom User Value */
798 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
799 time_t jointime; /*!< Time the user joined the conference */
800 time_t kicktime; /*!< Time the user will be kicked from the conference */
801 struct timeval start_time; /*!< Time the user entered into the conference */
802 long timelimit; /*!< Time limit for the user to be in the conference L(x:y:z) */
803 long play_warning; /*!< Play a warning when 'y' ms are left */
804 long warning_freq; /*!< Repeat the warning every 'z' ms */
805 const char *warning_sound; /*!< File to play as warning if 'y' is defined */
806 const char *end_sound; /*!< File to play when time is up. */
808 struct volume listen;
809 AST_LIST_ENTRY(ast_conf_user) list;
812 enum sla_which_trunk_refs {
817 enum sla_trunk_state {
818 SLA_TRUNK_STATE_IDLE,
819 SLA_TRUNK_STATE_RINGING,
821 SLA_TRUNK_STATE_ONHOLD,
822 SLA_TRUNK_STATE_ONHOLD_BYME,
825 enum sla_hold_access {
826 /*! This means that any station can put it on hold, and any station
827 * can retrieve the call from hold. */
829 /*! This means that only the station that put the call on hold may
830 * retrieve it from hold. */
834 struct sla_trunk_ref;
837 AST_RWLIST_ENTRY(sla_station) entry;
838 AST_DECLARE_STRING_FIELDS(
839 AST_STRING_FIELD(name);
840 AST_STRING_FIELD(device);
841 AST_STRING_FIELD(autocontext);
843 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
844 struct ast_dial *dial;
845 /*! Ring timeout for this station, for any trunk. If a ring timeout
846 * is set for a specific trunk on this station, that will take
847 * priority over this value. */
848 unsigned int ring_timeout;
849 /*! Ring delay for this station, for any trunk. If a ring delay
850 * is set for a specific trunk on this station, that will take
851 * priority over this value. */
852 unsigned int ring_delay;
853 /*! This option uses the values in the sla_hold_access enum and sets the
854 * access control type for hold on this station. */
855 unsigned int hold_access:1;
856 /*! Use count for inside sla_station_exec */
857 unsigned int ref_count;
860 struct sla_station_ref {
861 AST_LIST_ENTRY(sla_station_ref) entry;
862 struct sla_station *station;
866 AST_RWLIST_ENTRY(sla_trunk) entry;
867 AST_DECLARE_STRING_FIELDS(
868 AST_STRING_FIELD(name);
869 AST_STRING_FIELD(device);
870 AST_STRING_FIELD(autocontext);
872 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
873 /*! Number of stations that use this trunk */
874 unsigned int num_stations;
875 /*! Number of stations currently on a call with this trunk */
876 unsigned int active_stations;
877 /*! Number of stations that have this trunk on hold. */
878 unsigned int hold_stations;
879 struct ast_channel *chan;
880 unsigned int ring_timeout;
881 /*! If set to 1, no station will be able to join an active call with
883 unsigned int barge_disabled:1;
884 /*! This option uses the values in the sla_hold_access enum and sets the
885 * access control type for hold on this trunk. */
886 unsigned int hold_access:1;
887 /*! Whether this trunk is currently on hold, meaning that once a station
888 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
889 unsigned int on_hold:1;
890 /*! Use count for inside sla_trunk_exec */
891 unsigned int ref_count;
894 struct sla_trunk_ref {
895 AST_LIST_ENTRY(sla_trunk_ref) entry;
896 struct sla_trunk *trunk;
897 enum sla_trunk_state state;
898 struct ast_channel *chan;
899 /*! Ring timeout to use when this trunk is ringing on this specific
900 * station. This takes higher priority than a ring timeout set at
901 * the station level. */
902 unsigned int ring_timeout;
903 /*! Ring delay to use when this trunk is ringing on this specific
904 * station. This takes higher priority than a ring delay set at
905 * the station level. */
906 unsigned int ring_delay;
909 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
910 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
912 static const char sla_registrar[] = "SLA";
914 /*! \brief Event types that can be queued up for the SLA thread */
915 enum sla_event_type {
916 /*! A station has put the call on hold */
918 /*! The state of a dial has changed */
919 SLA_EVENT_DIAL_STATE,
920 /*! The state of a ringing trunk has changed */
921 SLA_EVENT_RINGING_TRUNK,
922 /*! A reload of configuration has been requested */
924 /*! Poke the SLA thread so it can check if it can perform a reload */
925 SLA_EVENT_CHECK_RELOAD,
929 enum sla_event_type type;
930 struct sla_station *station;
931 struct sla_trunk_ref *trunk_ref;
932 AST_LIST_ENTRY(sla_event) entry;
935 /*! \brief A station that failed to be dialed
936 * \note Only used by the SLA thread. */
937 struct sla_failed_station {
938 struct sla_station *station;
939 struct timeval last_try;
940 AST_LIST_ENTRY(sla_failed_station) entry;
943 /*! \brief A trunk that is ringing */
944 struct sla_ringing_trunk {
945 struct sla_trunk *trunk;
946 /*! The time that this trunk started ringing */
947 struct timeval ring_begin;
948 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
949 AST_LIST_ENTRY(sla_ringing_trunk) entry;
952 enum sla_station_hangup {
953 SLA_STATION_HANGUP_NORMAL,
954 SLA_STATION_HANGUP_TIMEOUT,
957 /*! \brief A station that is ringing */
958 struct sla_ringing_station {
959 struct sla_station *station;
960 /*! The time that this station started ringing */
961 struct timeval ring_begin;
962 AST_LIST_ENTRY(sla_ringing_station) entry;
966 * \brief A structure for data used by the sla thread
969 /*! The SLA thread ID */
973 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
974 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
975 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
976 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
978 /*! Attempt to handle CallerID, even though it is known not to work
979 * properly in some situations. */
980 unsigned int attempt_callerid:1;
981 /*! A reload has been requested */
982 unsigned int reload:1;
984 .thread = AST_PTHREADT_NULL,
987 /*! \brief The number of audio buffers to be allocated on pseudo channels
988 * when in a conference */
989 static int audio_buffers;
991 /*! \brief Map 'volume' levels from -5 through +5 into decibel (dB)
992 * settings for channel drivers.
994 * \note these are not a straight linear-to-dB
995 * conversion... the numbers have been modified
996 * to give the user a better level of adjustability.
998 static const char gain_map[] = {
1013 static int admin_exec(struct ast_channel *chan, const char *data);
1014 static void *recordthread(void *args);
1016 static const char *istalking(int x)
1021 return "(unmonitored)";
1023 return "(not talking)";
1026 static int careful_write(int fd, unsigned char *data, int len, int block)
1033 x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
1034 res = ioctl(fd, DAHDI_IOMUX, &x);
1038 res = write(fd, data, len);
1040 if (errno != EAGAIN) {
1041 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
1053 static int set_talk_volume(struct ast_conf_user *user, int volume)
1057 /* attempt to make the adjustment in the channel driver;
1058 if successful, don't adjust in the frame reading routine
1060 gain_adjust = gain_map[volume + 5];
1062 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1065 static int set_listen_volume(struct ast_conf_user *user, int volume)
1069 /* attempt to make the adjustment in the channel driver;
1070 if successful, don't adjust in the frame reading routine
1072 gain_adjust = gain_map[volume + 5];
1074 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1077 static void tweak_volume(struct volume *vol, enum volume_action action)
1081 switch (vol->desired) {
1096 switch (vol->desired) {
1112 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
1114 tweak_volume(&user->talk, action);
1115 /* attempt to make the adjustment in the channel driver;
1116 if successful, don't adjust in the frame reading routine
1118 if (!set_talk_volume(user, user->talk.desired))
1119 user->talk.actual = 0;
1121 user->talk.actual = user->talk.desired;
1124 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
1126 tweak_volume(&user->listen, action);
1127 /* attempt to make the adjustment in the channel driver;
1128 if successful, don't adjust in the frame reading routine
1130 if (!set_listen_volume(user, user->listen.desired))
1131 user->listen.actual = 0;
1133 user->listen.actual = user->listen.desired;
1136 static void reset_volumes(struct ast_conf_user *user)
1138 signed char zero_volume = 0;
1140 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
1141 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
1144 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
1146 unsigned char *data;
1150 if (!ast_check_hangup(chan))
1151 res = ast_autoservice_start(chan);
1153 AST_LIST_LOCK(&confs);
1158 len = sizeof(enter);
1162 len = sizeof(leave);
1169 careful_write(conf->fd, data, len, 1);
1172 AST_LIST_UNLOCK(&confs);
1175 ast_autoservice_stop(chan);
1178 static int user_no_cmp(void *obj, void *arg, int flags)
1180 struct ast_conf_user *user = obj;
1183 if (user->user_no == *user_no) {
1184 return (CMP_MATCH | CMP_STOP);
1190 static int user_max_cmp(void *obj, void *arg, int flags)
1192 struct ast_conf_user *user = obj;
1195 if (user->user_no > *max_no) {
1196 *max_no = user->user_no;
1203 * \brief Find or create a conference
1205 * \param confno The conference name/number
1206 * \param pin The regular user pin
1207 * \param pinadmin The admin pin
1208 * \param make Make the conf if it doesn't exist
1209 * \param dynamic Mark the newly created conference as dynamic
1210 * \param refcount How many references to mark on the conference
1211 * \param chan The asterisk channel
1213 * \return A pointer to the conference struct, or NULL if it wasn't found and
1214 * make or dynamic were not set.
1216 static struct ast_conference *build_conf(const char *confno, const char *pin,
1217 const char *pinadmin, int make, int dynamic, int refcount,
1218 const struct ast_channel *chan, struct ast_test *test)
1220 struct ast_conference *cnf;
1221 struct dahdi_confinfo dahdic = { 0, };
1223 struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
1224 struct ast_format tmp_fmt;
1226 AST_LIST_LOCK(&confs);
1228 AST_LIST_TRAVERSE(&confs, cnf, list) {
1229 if (!strcmp(confno, cnf->confno))
1233 if (cnf || (!make && !dynamic) || !cap_slin)
1236 ast_format_cap_add(cap_slin, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0));
1237 /* Make a new one */
1238 if (!(cnf = ast_calloc(1, sizeof(*cnf))) ||
1239 !(cnf->usercontainer = ao2_container_alloc(1, NULL, user_no_cmp))) {
1243 ast_mutex_init(&cnf->playlock);
1244 ast_mutex_init(&cnf->listenlock);
1245 cnf->recordthread = AST_PTHREADT_NULL;
1246 ast_mutex_init(&cnf->recordthreadlock);
1247 cnf->announcethread = AST_PTHREADT_NULL;
1248 ast_mutex_init(&cnf->announcethreadlock);
1249 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
1250 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
1251 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
1252 ast_copy_string(cnf->uniqueid, ast_channel_uniqueid(chan), sizeof(cnf->uniqueid));
1254 /* Setup a new dahdi conference */
1256 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1257 cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
1258 if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
1260 /* if we are creating a conference for a unit test, it is not neccesary
1261 * to open a pseudo channel, so, if we fail continue creating
1262 * the conference. */
1263 ast_test_status_update(test, "Unable to open DAHDI pseudo device\n");
1265 ast_log(LOG_WARNING, "Unable to open DAHDI pseudo device\n");
1268 ao2_ref(cnf->usercontainer, -1);
1269 ast_mutex_destroy(&cnf->playlock);
1270 ast_mutex_destroy(&cnf->listenlock);
1271 ast_mutex_destroy(&cnf->recordthreadlock);
1272 ast_mutex_destroy(&cnf->announcethreadlock);
1279 cnf->dahdiconf = dahdic.confno;
1281 /* Setup a new channel for playback of audio files */
1282 cnf->chan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL);
1284 ast_set_read_format_by_id(cnf->chan, AST_FORMAT_SLINEAR);
1285 ast_set_write_format_by_id(cnf->chan, AST_FORMAT_SLINEAR);
1287 dahdic.confno = cnf->dahdiconf;
1288 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1289 if (ioctl(ast_channel_fd(cnf->chan, 0), DAHDI_SETCONF, &dahdic)) {
1291 ast_test_status_update(test, "Error setting conference on pseudo channel\n");
1293 ast_log(LOG_WARNING, "Error setting conference\n");
1295 ast_hangup(cnf->chan);
1298 ao2_ref(cnf->usercontainer, -1);
1299 ast_mutex_destroy(&cnf->playlock);
1300 ast_mutex_destroy(&cnf->listenlock);
1301 ast_mutex_destroy(&cnf->recordthreadlock);
1302 ast_mutex_destroy(&cnf->announcethreadlock);
1309 /* Fill the conference struct */
1310 cnf->start = time(NULL);
1311 cnf->maxusers = 0x7fffffff;
1312 cnf->isdynamic = dynamic ? 1 : 0;
1313 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
1314 AST_LIST_INSERT_HEAD(&confs, cnf, list);
1316 /* Reserve conference number in map */
1317 if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1318 conf_map[confno_int] = 1;
1321 cap_slin = ast_format_cap_destroy(cap_slin);
1323 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
1325 AST_LIST_UNLOCK(&confs);
1330 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
1332 static const char * const cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
1334 int len = strlen(word);
1336 struct ast_conference *cnf = NULL;
1337 struct ast_conf_user *usr = NULL;
1338 char *confno = NULL;
1339 char usrno[50] = "";
1340 char *myline, *ret = NULL;
1342 if (pos == 1) { /* Command */
1343 return ast_cli_complete(word, cmds, state);
1344 } else if (pos == 2) { /* Conference Number */
1345 AST_LIST_LOCK(&confs);
1346 AST_LIST_TRAVERSE(&confs, cnf, list) {
1347 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
1352 ret = ast_strdup(ret); /* dup before releasing the lock */
1353 AST_LIST_UNLOCK(&confs);
1355 } else if (pos == 3) {
1356 /* User Number || Conf Command option*/
1357 if (strstr(line, "mute") || strstr(line, "kick")) {
1358 if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
1359 return ast_strdup("all");
1361 AST_LIST_LOCK(&confs);
1363 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
1364 myline = ast_strdupa(line);
1365 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
1366 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
1370 AST_LIST_TRAVERSE(&confs, cnf, list) {
1371 if (!strcmp(confno, cnf->confno))
1376 struct ao2_iterator user_iter;
1377 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
1379 while((usr = ao2_iterator_next(&user_iter))) {
1380 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
1381 if (!strncasecmp(word, usrno, len) && ++which > state) {
1387 ao2_iterator_destroy(&user_iter);
1388 AST_LIST_UNLOCK(&confs);
1389 return usr ? ast_strdup(usrno) : NULL;
1391 AST_LIST_UNLOCK(&confs);
1398 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1400 /* Process the command */
1401 struct ast_conf_user *user;
1402 struct ast_conference *cnf;
1404 int i = 0, total = 0;
1406 struct ast_str *cmdline = NULL;
1407 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
1408 #define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
1412 e->command = "meetme list [concise]";
1414 "Usage: meetme list [concise] <confno> \n"
1415 " List all or a specific conference.\n";
1418 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1421 /* Check for length so no buffer will overflow... */
1422 for (i = 0; i < a->argc; i++) {
1423 if (strlen(a->argv[i]) > 100)
1424 ast_cli(a->fd, "Invalid Arguments.\n");
1427 /* Max confno length */
1428 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1432 if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], "concise"))) {
1433 /* List all the conferences */
1434 int concise = (a->argc == 3 && !strcasecmp(a->argv[2], "concise"));
1436 AST_LIST_LOCK(&confs);
1437 if (AST_LIST_EMPTY(&confs)) {
1439 ast_cli(a->fd, "No active MeetMe conferences.\n");
1441 AST_LIST_UNLOCK(&confs);
1446 ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
1448 AST_LIST_TRAVERSE(&confs, cnf, list) {
1449 if (cnf->markedusers == 0) {
1450 ast_str_set(&cmdline, 0, "N/A ");
1452 ast_str_set(&cmdline, 0, "%4.4d", cnf->markedusers);
1454 hr = (now - cnf->start) / 3600;
1455 min = ((now - cnf->start) % 3600) / 60;
1456 sec = (now - cnf->start) % 60;
1458 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");
1460 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1469 total += cnf->users;
1471 AST_LIST_UNLOCK(&confs);
1473 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1477 } else if (strcmp(a->argv[1], "list") == 0) {
1478 struct ao2_iterator user_iter;
1479 int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
1480 /* List all the users in a conference */
1481 if (AST_LIST_EMPTY(&confs)) {
1483 ast_cli(a->fd, "No active MeetMe conferences.\n");
1488 /* Find the right conference */
1489 AST_LIST_LOCK(&confs);
1490 AST_LIST_TRAVERSE(&confs, cnf, list) {
1491 if (strcmp(cnf->confno, a->argv[2]) == 0) {
1497 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
1498 AST_LIST_UNLOCK(&confs);
1502 /* Show all the users */
1504 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
1505 while((user = ao2_iterator_next(&user_iter))) {
1506 hr = (now - user->jointime) / 3600;
1507 min = ((now - user->jointime) % 3600) / 60;
1508 sec = (now - user->jointime) % 60;
1510 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1512 S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
1513 S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<no name>"),
1514 ast_channel_name(user->chan),
1515 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "(Admin)" : "",
1516 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "(Listen only)" : "",
1517 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1518 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1519 istalking(user->talking), hr, min, sec);
1521 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1523 S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, ""),
1524 S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, ""),
1525 ast_channel_name(user->chan),
1526 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "1" : "",
1527 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "1" : "",
1528 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1529 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1530 user->talking, hr, min, sec);
1534 ao2_iterator_destroy(&user_iter);
1536 ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
1538 AST_LIST_UNLOCK(&confs);
1544 return CLI_SHOWUSAGE;
1547 ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
1549 admin_exec(NULL, ast_str_buffer(cmdline));
1556 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1558 /* Process the command */
1559 struct ast_str *cmdline = NULL;
1564 e->command = "meetme {lock|unlock|mute|unmute|kick}";
1566 "Usage: meetme (un)lock|(un)mute|kick <confno> <usernumber>\n"
1567 " Executes a command for the conference or on a conferee\n";
1570 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1574 ast_cli(a->fd, "Invalid Arguments.\n");
1575 /* Check for length so no buffer will overflow... */
1576 for (i = 0; i < a->argc; i++) {
1577 if (strlen(a->argv[i]) > 100)
1578 ast_cli(a->fd, "Invalid Arguments.\n");
1581 /* Max confno length */
1582 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1588 return CLI_SHOWUSAGE;
1591 ast_str_set(&cmdline, 0, "%s", a->argv[2]); /* Argv 2: conference number */
1592 if (strstr(a->argv[1], "lock")) {
1593 if (strcmp(a->argv[1], "lock") == 0) {
1595 ast_str_append(&cmdline, 0, ",L");
1598 ast_str_append(&cmdline, 0, ",l");
1600 } else if (strstr(a->argv[1], "mute")) {
1603 return CLI_SHOWUSAGE;
1605 if (strcmp(a->argv[1], "mute") == 0) {
1607 if (strcmp(a->argv[3], "all") == 0) {
1608 ast_str_append(&cmdline, 0, ",N");
1610 ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);
1614 if (strcmp(a->argv[3], "all") == 0) {
1615 ast_str_append(&cmdline, 0, ",n");
1617 ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
1620 } else if (strcmp(a->argv[1], "kick") == 0) {
1623 return CLI_SHOWUSAGE;
1625 if (strcmp(a->argv[3], "all") == 0) {
1627 ast_str_append(&cmdline, 0, ",K");
1629 /* Kick a single user */
1630 ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
1634 return CLI_SHOWUSAGE;
1637 ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
1639 admin_exec(NULL, ast_str_buffer(cmdline));
1645 static const char *sla_hold_str(unsigned int hold_access)
1647 const char *hold = "Unknown";
1649 switch (hold_access) {
1653 case SLA_HOLD_PRIVATE:
1662 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1664 const struct sla_trunk *trunk;
1668 e->command = "sla show trunks";
1670 "Usage: sla show trunks\n"
1671 " This will list all trunks defined in sla.conf\n";
1678 "=============================================================\n"
1679 "=== Configured SLA Trunks ===================================\n"
1680 "=============================================================\n"
1682 AST_RWLIST_RDLOCK(&sla_trunks);
1683 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1684 struct sla_station_ref *station_ref;
1685 char ring_timeout[16] = "(none)";
1686 if (trunk->ring_timeout)
1687 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1688 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1689 "=== Trunk Name: %s\n"
1690 "=== ==> Device: %s\n"
1691 "=== ==> AutoContext: %s\n"
1692 "=== ==> RingTimeout: %s\n"
1693 "=== ==> BargeAllowed: %s\n"
1694 "=== ==> HoldAccess: %s\n"
1695 "=== ==> Stations ...\n",
1696 trunk->name, trunk->device,
1697 S_OR(trunk->autocontext, "(none)"),
1699 trunk->barge_disabled ? "No" : "Yes",
1700 sla_hold_str(trunk->hold_access));
1701 AST_RWLIST_RDLOCK(&sla_stations);
1702 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1703 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
1704 AST_RWLIST_UNLOCK(&sla_stations);
1705 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
1707 AST_RWLIST_UNLOCK(&sla_trunks);
1708 ast_cli(a->fd, "=============================================================\n\n");
1713 static const char *trunkstate2str(enum sla_trunk_state state)
1715 #define S(e) case e: return # e;
1717 S(SLA_TRUNK_STATE_IDLE)
1718 S(SLA_TRUNK_STATE_RINGING)
1719 S(SLA_TRUNK_STATE_UP)
1720 S(SLA_TRUNK_STATE_ONHOLD)
1721 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1723 return "Uknown State";
1727 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1729 const struct sla_station *station;
1733 e->command = "sla show stations";
1735 "Usage: sla show stations\n"
1736 " This will list all stations defined in sla.conf\n";
1743 "=============================================================\n"
1744 "=== Configured SLA Stations =================================\n"
1745 "=============================================================\n"
1747 AST_RWLIST_RDLOCK(&sla_stations);
1748 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1749 struct sla_trunk_ref *trunk_ref;
1750 char ring_timeout[16] = "(none)";
1751 char ring_delay[16] = "(none)";
1752 if (station->ring_timeout) {
1753 snprintf(ring_timeout, sizeof(ring_timeout),
1754 "%u", station->ring_timeout);
1756 if (station->ring_delay) {
1757 snprintf(ring_delay, sizeof(ring_delay),
1758 "%u", station->ring_delay);
1760 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1761 "=== Station Name: %s\n"
1762 "=== ==> Device: %s\n"
1763 "=== ==> AutoContext: %s\n"
1764 "=== ==> RingTimeout: %s\n"
1765 "=== ==> RingDelay: %s\n"
1766 "=== ==> HoldAccess: %s\n"
1767 "=== ==> Trunks ...\n",
1768 station->name, station->device,
1769 S_OR(station->autocontext, "(none)"),
1770 ring_timeout, ring_delay,
1771 sla_hold_str(station->hold_access));
1772 AST_RWLIST_RDLOCK(&sla_trunks);
1773 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1774 if (trunk_ref->ring_timeout) {
1775 snprintf(ring_timeout, sizeof(ring_timeout),
1776 "%u", trunk_ref->ring_timeout);
1778 strcpy(ring_timeout, "(none)");
1779 if (trunk_ref->ring_delay) {
1780 snprintf(ring_delay, sizeof(ring_delay),
1781 "%u", trunk_ref->ring_delay);
1783 strcpy(ring_delay, "(none)");
1784 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
1785 "=== ==> State: %s\n"
1786 "=== ==> RingTimeout: %s\n"
1787 "=== ==> RingDelay: %s\n",
1788 trunk_ref->trunk->name,
1789 trunkstate2str(trunk_ref->state),
1790 ring_timeout, ring_delay);
1792 AST_RWLIST_UNLOCK(&sla_trunks);
1793 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1796 AST_RWLIST_UNLOCK(&sla_stations);
1797 ast_cli(a->fd, "============================================================\n"
1803 static struct ast_cli_entry cli_meetme[] = {
1804 AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
1805 AST_CLI_DEFINE(meetme_show_cmd, "List all or one conference"),
1806 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
1807 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
1810 static void conf_flush(int fd, struct ast_channel *chan)
1814 /* read any frames that may be waiting on the channel
1818 struct ast_frame *f;
1820 /* when no frames are available, this will wait
1821 for 1 millisecond maximum
1823 while (ast_waitfor(chan, 1)) {
1827 else /* channel was hung up or something else happened */
1832 /* flush any data sitting in the pseudo channel */
1833 x = DAHDI_FLUSH_ALL;
1834 if (ioctl(fd, DAHDI_FLUSH, &x))
1835 ast_log(LOG_WARNING, "Error flushing channel\n");
1839 /*! \brief Remove the conference from the list and free it.
1841 We assume that this was called while holding conflock. */
1842 static int conf_free(struct ast_conference *conf)
1845 struct announce_listitem *item;
1847 AST_LIST_REMOVE(&confs, conf, list);
1849 <managerEventInstance>
1850 <synopsis>Raised when a MeetMe conference ends.</synopsis>
1852 <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
1855 <ref type="managerEvent">MeetmeJoin</ref>
1857 </managerEventInstance>
1859 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1861 if (conf->recording == MEETME_RECORD_ACTIVE) {
1862 conf->recording = MEETME_RECORD_TERMINATE;
1863 AST_LIST_UNLOCK(&confs);
1866 AST_LIST_LOCK(&confs);
1867 if (conf->recording == MEETME_RECORD_OFF)
1869 AST_LIST_UNLOCK(&confs);
1873 for (x = 0; x < AST_FRAME_BITS; x++) {
1874 if (conf->transframe[x])
1875 ast_frfree(conf->transframe[x]);
1876 if (conf->transpath[x])
1877 ast_translator_free_path(conf->transpath[x]);
1879 if (conf->announcethread != AST_PTHREADT_NULL) {
1880 ast_mutex_lock(&conf->announcelistlock);
1881 conf->announcethread_stop = 1;
1882 ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
1883 ast_cond_signal(&conf->announcelist_addition);
1884 ast_mutex_unlock(&conf->announcelistlock);
1885 pthread_join(conf->announcethread, NULL);
1887 while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
1888 /* If it's a voicemail greeting file we don't want to remove it */
1890 ast_filedelete(item->namerecloc, NULL);
1894 ast_mutex_destroy(&conf->announcelistlock);
1897 if (conf->origframe)
1898 ast_frfree(conf->origframe);
1900 ast_hangup(conf->lchan);
1902 ast_hangup(conf->chan);
1905 if (conf->recordingfilename) {
1906 ast_free(conf->recordingfilename);
1908 if (conf->usercontainer) {
1909 ao2_ref(conf->usercontainer, -1);
1911 if (conf->recordingformat) {
1912 ast_free(conf->recordingformat);
1914 ast_mutex_destroy(&conf->playlock);
1915 ast_mutex_destroy(&conf->listenlock);
1916 ast_mutex_destroy(&conf->recordthreadlock);
1917 ast_mutex_destroy(&conf->announcethreadlock);
1923 static void conf_queue_dtmf(const struct ast_conference *conf,
1924 const struct ast_conf_user *sender, struct ast_frame *f)
1926 struct ast_conf_user *user;
1927 struct ao2_iterator user_iter;
1929 user_iter = ao2_iterator_init(conf->usercontainer, 0);
1930 while ((user = ao2_iterator_next(&user_iter))) {
1931 if (user == sender) {
1935 if (ast_write(user->chan, f) < 0)
1936 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", ast_channel_name(user->chan));
1939 ao2_iterator_destroy(&user_iter);
1942 static void sla_queue_event_full(enum sla_event_type type,
1943 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1945 struct sla_event *event;
1947 if (sla.thread == AST_PTHREADT_NULL) {
1951 if (!(event = ast_calloc(1, sizeof(*event))))
1955 event->trunk_ref = trunk_ref;
1956 event->station = station;
1959 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1963 ast_mutex_lock(&sla.lock);
1964 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1965 ast_cond_signal(&sla.cond);
1966 ast_mutex_unlock(&sla.lock);
1969 static void sla_queue_event_nolock(enum sla_event_type type)
1971 sla_queue_event_full(type, NULL, NULL, 0);
1974 static void sla_queue_event(enum sla_event_type type)
1976 sla_queue_event_full(type, NULL, NULL, 1);
1979 /*! \brief Queue a SLA event from the conference */
1980 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1981 struct ast_conference *conf)
1983 struct sla_station *station;
1984 struct sla_trunk_ref *trunk_ref = NULL;
1987 trunk_name = ast_strdupa(conf->confno);
1988 strsep(&trunk_name, "_");
1989 if (ast_strlen_zero(trunk_name)) {
1990 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1994 AST_RWLIST_RDLOCK(&sla_stations);
1995 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1996 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1997 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
2003 AST_RWLIST_UNLOCK(&sla_stations);
2006 ast_debug(1, "Trunk not found for event!\n");
2010 sla_queue_event_full(type, trunk_ref, station, 1);
2013 /*! \brief Decrement reference counts, as incremented by find_conf() */
2014 static int dispose_conf(struct ast_conference *conf)
2019 AST_LIST_LOCK(&confs);
2020 if (ast_atomic_dec_and_test(&conf->refcount)) {
2021 /* Take the conference room number out of an inuse state */
2022 if ((sscanf(conf->confno, "%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
2023 conf_map[confno_int] = 0;
2028 AST_LIST_UNLOCK(&confs);
2033 static int rt_extend_conf(const char *confno)
2035 char currenttime[32];
2039 struct ast_variable *var, *orig_var;
2048 ast_localtime(&now, &tm, NULL);
2049 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2051 var = ast_load_realtime("meetme", "confno",
2052 confno, "startTime<= ", currenttime,
2053 "endtime>= ", currenttime, NULL);
2057 /* Identify the specific RealTime conference */
2059 if (!strcasecmp(var->name, "bookid")) {
2060 ast_copy_string(bookid, var->value, sizeof(bookid));
2062 if (!strcasecmp(var->name, "endtime")) {
2063 ast_copy_string(endtime, var->value, sizeof(endtime));
2068 ast_variables_destroy(orig_var);
2070 ast_strptime(endtime, DATE_FORMAT, &tm);
2071 now = ast_mktime(&tm, NULL);
2073 now.tv_sec += extendby;
2075 ast_localtime(&now, &tm, NULL);
2076 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2077 strcat(currenttime, "0"); /* Seconds needs to be 00 */
2079 var = ast_load_realtime("meetme", "confno",
2080 confno, "startTime<= ", currenttime,
2081 "endtime>= ", currenttime, NULL);
2083 /* If there is no conflict with extending the conference, update the DB */
2085 ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
2086 ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
2091 ast_variables_destroy(var);
2095 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
2099 ast_channel_lock(chan);
2100 original_moh = ast_strdupa(ast_channel_musicclass(chan));
2101 ast_channel_musicclass_set(chan, musicclass);
2102 ast_channel_unlock(chan);
2104 ast_moh_start(chan, original_moh, NULL);
2106 ast_channel_lock(chan);
2107 ast_channel_musicclass_set(chan, original_moh);
2108 ast_channel_unlock(chan);
2111 static const char *get_announce_filename(enum announcetypes type)
2115 return "conf-hasleft";
2118 return "conf-hasjoin";
2125 static void *announce_thread(void *data)
2127 struct announce_listitem *current;
2128 struct ast_conference *conf = data;
2130 char filename[PATH_MAX] = "";
2131 AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
2132 AST_LIST_HEAD_INIT_NOLOCK(&local_list);
2134 while (!conf->announcethread_stop) {
2135 ast_mutex_lock(&conf->announcelistlock);
2136 if (conf->announcethread_stop) {
2137 ast_mutex_unlock(&conf->announcelistlock);
2140 if (AST_LIST_EMPTY(&conf->announcelist))
2141 ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
2143 AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
2144 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2146 ast_mutex_unlock(&conf->announcelistlock);
2147 if (conf->announcethread_stop) {
2151 for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
2152 ast_debug(1, "About to play %s\n", current->namerecloc);
2153 if (!ast_fileexists(current->namerecloc, NULL, NULL))
2155 if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
2156 if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
2157 res = ast_waitstream(current->confchan, "");
2159 ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
2160 if (!ast_streamfile(current->confchan, filename, current->language))
2161 ast_waitstream(current->confchan, "");
2164 if (current->announcetype == CONF_HASLEFT && current->announcetype && !current->vmrec) {
2165 /* only remove it if it isn't a VM recording file */
2166 ast_filedelete(current->namerecloc, NULL);
2171 /* thread marked to stop, clean up */
2172 while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
2173 /* only delete if it's a vm rec */
2174 if (!current->vmrec) {
2175 ast_filedelete(current->namerecloc, NULL);
2177 ao2_ref(current, -1);
2182 static int can_write(struct ast_channel *chan, struct ast_flags64 *confflags)
2184 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
2188 return (ast_channel_state(chan) == AST_STATE_UP);
2191 static void send_talking_event(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking)
2194 <managerEventInstance>
2195 <synopsis>Raised when a MeetMe user begins or ends talking.</synopsis>
2197 <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
2198 <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Usernum'])" />
2199 <parameter name="Status">
2206 </managerEventInstance>
2208 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalking",
2214 ast_channel_name(chan), ast_channel_uniqueid(chan),
2216 user->user_no, talking ? "on" : "off");
2219 static void set_user_talking(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor)
2221 int last_talking = user->talking;
2222 if (last_talking == talking)
2225 user->talking = talking;
2228 /* Check if talking state changed. Take care of -1 which means unmonitored */
2229 int was_talking = (last_talking > 0);
2230 int now_talking = (talking > 0);
2231 if (was_talking != now_talking) {
2232 send_talking_event(chan, conf, user, now_talking);
2237 static int user_set_hangup_cb(void *obj, void *check_admin_arg, int flags)
2239 struct ast_conf_user *user = obj;
2240 /* actual pointer contents of check_admin_arg is irrelevant */
2242 if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
2243 user->adminflags |= ADMINFLAG_HANGUP;
2248 static int user_set_kickme_cb(void *obj, void *check_admin_arg, int flags)
2250 struct ast_conf_user *user = obj;
2251 /* actual pointer contents of check_admin_arg is irrelevant */
2253 if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
2254 user->adminflags |= ADMINFLAG_KICKME;
2259 static int user_set_unmuted_cb(void *obj, void *check_admin_arg, int flags)
2261 struct ast_conf_user *user = obj;
2262 /* actual pointer contents of check_admin_arg is irrelevant */
2264 if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
2265 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
2270 static int user_set_muted_cb(void *obj, void *check_admin_arg, int flags)
2272 struct ast_conf_user *user = obj;
2273 /* actual pointer contents of check_admin_arg is irrelevant */
2275 if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
2276 user->adminflags |= ADMINFLAG_MUTED;
2281 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struct ast_flags64 *confflags, char *optargs[])
2283 struct ast_conf_user *user = NULL;
2285 struct dahdi_confinfo dahdic, dahdic_empty;
2286 struct ast_frame *f;
2287 struct ast_channel *c;
2288 struct ast_frame fr;
2295 int musiconhold = 0, mohtempstopped = 0;
2298 int currentmarked = 0;
2301 int menu_active = 0;
2302 int menu8_active = 0;
2303 int talkreq_manager = 0;
2304 int using_pseudo = 0;
2308 int announcement_played = 0;
2310 struct ast_dsp *dsp = NULL;
2311 struct ast_app *agi_app;
2312 char *agifile, *mod_speex;
2313 const char *agifiledefault = "conf-background.agi", *tmpvar;
2314 char meetmesecs[30] = "";
2315 char exitcontext[AST_MAX_CONTEXT] = "";
2316 char recordingtmp[AST_MAX_EXTENSION] = "";
2317 char members[10] = "";
2318 int dtmf, opt_waitmarked_timeout = 0;
2320 struct dahdi_bufferinfo bi;
2321 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
2322 char *buf = __buf + AST_FRIENDLY_OFFSET;
2323 char *exitkeys = NULL;
2324 unsigned int calldurationlimit = 0;
2326 long play_warning = 0;
2327 long warning_freq = 0;
2328 const char *warning_sound = NULL;
2329 const char *end_sound = NULL;
2331 long time_left_ms = 0;
2332 struct timeval nexteventts = { 0, };
2334 int setusercount = 0;
2335 int confsilence = 0, totalsilence = 0;
2336 char *mailbox, *context;
2337 struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
2338 struct ast_format tmpfmt;
2341 goto conf_run_cleanup;
2343 ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
2345 if (!(user = ao2_alloc(sizeof(*user), NULL))) {
2346 goto conf_run_cleanup;
2349 /* Possible timeout waiting for marked user */
2350 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
2351 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
2352 (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
2353 (opt_waitmarked_timeout > 0)) {
2354 timeout = time(NULL) + opt_waitmarked_timeout;
2357 if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
2358 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
2359 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
2362 if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
2363 char *limit_str, *warning_str, *warnfreq_str;
2366 parse = optargs[OPT_ARG_DURATION_LIMIT];
2367 limit_str = strsep(&parse, ":");
2368 warning_str = strsep(&parse, ":");
2369 warnfreq_str = parse;
2371 timelimit = atol(limit_str);
2373 play_warning = atol(warning_str);
2375 warning_freq = atol(warnfreq_str);
2378 timelimit = play_warning = warning_freq = 0;
2379 warning_sound = NULL;
2380 } else if (play_warning > timelimit) {
2381 if (!warning_freq) {
2384 while (play_warning > timelimit)
2385 play_warning -= warning_freq;
2386 if (play_warning < 1)
2387 play_warning = warning_freq = 0;
2391 ast_verb(3, "Setting conference duration limit to: %ldms.\n", timelimit);
2393 ast_verb(3, "Setting warning time to %ldms from the conference duration limit.\n", play_warning);
2396 ast_verb(3, "Setting warning frequency to %ldms.\n", warning_freq);
2399 ast_channel_lock(chan);
2400 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
2401 var = ast_strdupa(var);
2403 ast_channel_unlock(chan);
2405 warning_sound = var ? var : "timeleft";
2407 ast_channel_lock(chan);
2408 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
2409 var = ast_strdupa(var);
2411 ast_channel_unlock(chan);
2413 end_sound = var ? var : NULL;
2415 /* undo effect of S(x) in case they are both used */
2416 calldurationlimit = 0;
2417 /* more efficient do it like S(x) does since no advanced opts */
2418 if (!play_warning && !end_sound && timelimit) {
2419 calldurationlimit = timelimit / 1000;
2420 timelimit = play_warning = warning_freq = 0;
2422 ast_debug(2, "Limit Data for this call:\n");
2423 ast_debug(2, "- timelimit = %ld\n", timelimit);
2424 ast_debug(2, "- play_warning = %ld\n", play_warning);
2425 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
2426 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
2427 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
2432 if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
2433 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
2434 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
2436 exitkeys = ast_strdupa("#"); /* Default */
2439 if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
2440 if (!conf->recordingfilename) {
2442 ast_channel_lock(chan);
2443 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
2444 conf->recordingfilename = ast_strdup(var);
2446 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
2447 conf->recordingformat = ast_strdup(var);
2449 ast_channel_unlock(chan);
2450 if (!conf->recordingfilename) {
2451 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan));
2452 conf->recordingfilename = ast_strdup(recordingtmp);
2454 if (!conf->recordingformat) {
2455 conf->recordingformat = ast_strdup("wav");
2457 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
2458 conf->confno, conf->recordingfilename, conf->recordingformat);
2462 ast_mutex_lock(&conf->recordthreadlock);
2463 if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
2464 ((conf->lchan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL)))) {
2465 ast_set_read_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
2466 ast_set_write_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
2468 dahdic.confno = conf->dahdiconf;
2469 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
2470 if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, &dahdic)) {
2471 ast_log(LOG_WARNING, "Error starting listen channel\n");
2472 ast_hangup(conf->lchan);
2475 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
2478 ast_mutex_unlock(&conf->recordthreadlock);
2480 ast_mutex_lock(&conf->announcethreadlock);
2481 if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
2482 ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC)) {
2483 ast_mutex_init(&conf->announcelistlock);
2484 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2485 ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
2487 ast_mutex_unlock(&conf->announcethreadlock);
2489 time(&user->jointime);
2491 user->timelimit = timelimit;
2492 user->play_warning = play_warning;
2493 user->warning_freq = warning_freq;
2494 user->warning_sound = warning_sound;
2495 user->end_sound = end_sound;
2497 if (calldurationlimit > 0) {
2498 time(&user->kicktime);
2499 user->kicktime = user->kicktime + calldurationlimit;
2502 if (ast_tvzero(user->start_time))
2503 user->start_time = ast_tvnow();
2504 time_left_ms = user->timelimit;
2506 if (user->timelimit) {
2507 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2508 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
2511 if (conf->locked && (!ast_test_flag64(confflags, CONFFLAG_ADMIN))) {
2512 /* Sorry, but this conference is locked! */
2513 if (!ast_streamfile(chan, "conf-locked", ast_channel_language(chan)))
2514 ast_waitstream(chan, "");
2518 ast_mutex_lock(&conf->playlock);
2520 if (rt_schedule && conf->maxusers) {
2521 if (conf->users >= conf->maxusers) {
2522 /* Sorry, but this confernce has reached the participant limit! */
2523 if (!ast_streamfile(chan, "conf-full", ast_channel_language(chan)))
2524 ast_waitstream(chan, "");
2525 ast_mutex_unlock(&conf->playlock);
2530 ao2_lock(conf->usercontainer);
2531 ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &user->user_no);
2533 ao2_link(conf->usercontainer, user);
2534 ao2_unlock(conf->usercontainer);
2537 user->userflags = *confflags;
2538 user->adminflags = ast_test_flag64(confflags, CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
2539 user->adminflags |= (conf->gmuted) ? ADMINFLAG_MUTED : 0;
2542 ast_mutex_unlock(&conf->playlock);
2544 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC))) {
2545 char destdir[PATH_MAX];
2547 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
2549 if (ast_mkdir(destdir, 0777) != 0) {
2550 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
2554 if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
2555 context = ast_strdupa(optargs[OPT_ARG_INTROUSER_VMREC]);
2556 mailbox = strsep(&context, "@");
2558 if (ast_strlen_zero(mailbox)) {
2559 /* invalid input, clear the v flag*/
2560 ast_clear_flag64(confflags,CONFFLAG_INTROUSER_VMREC);
2561 ast_log(LOG_WARNING,"You must specify a mailbox in the v() option\n");
2563 if (ast_strlen_zero(context)) {
2564 context = "default";
2566 /* if there is no mailbox we don't need to do this logic */
2567 snprintf(user->namerecloc, sizeof(user->namerecloc),
2568 "%s/voicemail/%s/%s/greet",ast_config_AST_SPOOL_DIR,context,mailbox);
2570 /* if the greeting doesn't exist then use the temp file method instead, clear flag v */
2571 if (!ast_fileexists(user->namerecloc, NULL, NULL)){
2572 snprintf(user->namerecloc, sizeof(user->namerecloc),
2573 "%s/meetme-username-%s-%d", destdir,
2574 conf->confno, user->user_no);
2575 ast_clear_flag64(confflags, CONFFLAG_INTROUSER_VMREC);
2579 snprintf(user->namerecloc, sizeof(user->namerecloc),
2580 "%s/meetme-username-%s-%d", destdir,
2581 conf->confno, user->user_no);
2585 if (ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) && !ast_fileexists(user->namerecloc, NULL, NULL))
2586 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
2587 else if (ast_test_flag64(confflags, CONFFLAG_INTROUSER) && !ast_fileexists(user->namerecloc, NULL, NULL))
2588 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
2594 ast_mutex_lock(&conf->playlock);
2596 if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER))
2597 conf->markedusers++;
2599 if (rt_log_members) {
2601 snprintf(members, sizeof(members), "%d", conf->users);
2602 ast_realtime_require_field("meetme",
2603 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
2604 "members", RQ_UINTEGER1, strlen(members),
2606 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2610 /* This device changed state now - if this is the first user */
2611 if (conf->users == 1)
2612 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
2614 ast_mutex_unlock(&conf->playlock);
2616 /* return the unique ID of the conference */
2617 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
2619 if (ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT)) {
2620 ast_channel_lock(chan);
2621 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
2622 ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
2623 } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))) {
2624 ast_copy_string(exitcontext, ast_channel_macrocontext(chan), sizeof(exitcontext));
2626 ast_copy_string(exitcontext, ast_channel_context(chan), sizeof(exitcontext));
2628 ast_channel_unlock(chan);
2631 /* Play an arbitrary intro message */
2632 if (ast_test_flag64(confflags, CONFFLAG_INTROMSG) &&
2633 !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
2634 if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], ast_channel_language(chan))) {
2635 ast_waitstream(chan, "");
2639 if (!ast_test_flag64(confflags, (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
2640 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED))
2641 if (!ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan)))
2642 ast_waitstream(chan, "");
2643 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && conf->markedusers == 0)
2644 if (!ast_streamfile(chan, "conf-waitforleader", ast_channel_language(chan)))
2645 ast_waitstream(chan, "");
2648 if (ast_test_flag64(confflags, CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
2649 int keepplaying = 1;
2651 if (conf->users == 2) {
2652 if (!ast_streamfile(chan, "conf-onlyone", ast_channel_language(chan))) {
2653 res = ast_waitstream(chan, AST_DIGIT_ANY);
2654 ast_stopstream(chan);
2661 if (!ast_streamfile(chan, "conf-thereare", ast_channel_language(chan))) {
2662 res = ast_waitstream(chan, AST_DIGIT_ANY);
2663 ast_stopstream(chan);
2670 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
2676 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", ast_channel_language(chan))) {
2677 res = ast_waitstream(chan, AST_DIGIT_ANY);
2678 ast_stopstream(chan);
2687 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
2688 /* We're leaving this alone until the state gets changed to up */
2689 ast_indicate(chan, -1);
2692 if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
2693 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", ast_channel_name(chan));
2697 if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
2698 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", ast_channel_name(chan));
2702 /* Reduce background noise from each participant */
2703 if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
2704 ast_free(mod_speex);
2705 ast_func_write(chan, "DENOISE(rx)", "on");
2708 retrydahdi = (strcasecmp(ast_channel_tech(chan)->type, "DAHDI") || (ast_channel_audiohooks(chan) || ast_channel_monitor(chan)) ? 1 : 0);
2709 user->dahdichannel = !retrydahdi;
2712 origfd = ast_channel_fd(chan, 0);
2714 /* open pseudo in non-blocking mode */
2715 fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
2717 ast_log(LOG_WARNING, "Unable to open DAHDI pseudo channel: %s\n", strerror(errno));
2721 /* Setup buffering information */
2722 memset(&bi, 0, sizeof(bi));
2723 bi.bufsize = CONF_SIZE / 2;
2724 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
2725 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
2726 bi.numbufs = audio_buffers;
2727 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
2728 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
2733 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
2734 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
2740 /* XXX Make sure we're not running on a pseudo channel XXX */
2741 fd = ast_channel_fd(chan, 0);
2744 memset(&dahdic, 0, sizeof(dahdic));
2745 memset(&dahdic_empty, 0, sizeof(dahdic_empty));
2746 /* Check to see if we're in a conference... */
2748 if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
2749 ast_log(LOG_WARNING, "Error getting conference\n");
2753 if (dahdic.confmode) {
2754 /* Whoa, already in a conference... Retry... */
2756 ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
2761 memset(&dahdic, 0, sizeof(dahdic));
2762 /* Add us to the conference */
2764 dahdic.confno = conf->dahdiconf;
2766 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
2767 ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) || ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)) && conf->users > 1) {
2768 struct announce_listitem *item;
2769 if (!(item = ao2_alloc(sizeof(*item), NULL)))
2771 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
2772 ast_copy_string(item->language, ast_channel_language(chan), sizeof(item->language));
2773 item->confchan = conf->chan;
2774 item->confusers = conf->users;
2775 if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
2778 item->announcetype = CONF_HASJOIN;
2779 ast_mutex_lock(&conf->announcelistlock);
2780 ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
2781 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
2782 ast_cond_signal(&conf->announcelist_addition);
2783 ast_mutex_unlock(&conf->announcelistlock);
2785 while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
2791 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && !conf->markedusers)
2792 dahdic.confmode = DAHDI_CONF_CONF;
2793 else if (ast_test_flag64(confflags, CONFFLAG_MONITOR))
2794 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2795 else if (ast_test_flag64(confflags, CONFFLAG_TALKER))
2796 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2798 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2800 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2801 ast_log(LOG_WARNING, "Error setting conference\n");
2805 ast_debug(1, "Placed channel %s in DAHDI conf %d\n", ast_channel_name(chan), conf->dahdiconf);
2809 <managerEventInstance>
2810 <synopsis>Raised when a user joins a MeetMe conference.</synopsis>
2812 <parameter name="Meetme">
2813 <para>The identifier for the MeetMe conference.</para>
2815 <parameter name="Usernum">
2816 <para>The identifier of the MeetMe user who joined.</para>
2820 <ref type="managerEvent">MeetmeLeave</ref>
2821 <ref type="application">MeetMe</ref>
2823 </managerEventInstance>
2825 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeJoin",
2830 "CallerIDnum: %s\r\n"
2831 "CallerIDname: %s\r\n"
2832 "ConnectedLineNum: %s\r\n"
2833 "ConnectedLineName: %s\r\n",
2834 ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno,
2836 S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
2837 S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<unknown>"),
2838 S_COR(ast_channel_connected(user->chan)->id.number.valid, ast_channel_connected(user->chan)->id.number.str, "<unknown>"),
2839 S_COR(ast_channel_connected(user->chan)->id.name.valid, ast_channel_connected(user->chan)->id.name.str, "<unknown>")
2844 if (!firstpass && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
2845 !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
2847 if (!ast_test_flag64(confflags, CONFFLAG_QUIET))
2848 if (!ast_test_flag64(confflags, CONFFLAG_WAITMARKED) || (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
2849 (conf->markedusers >= 1))) {
2850 conf_play(chan, conf, ENTER);
2854 conf_flush(fd, chan);
2859 if (!(dsp = ast_dsp_new())) {
2860 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
2864 if (ast_test_flag64(confflags, CONFFLAG_AGI)) {
2865 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
2866 or use default filename of conf-background.agi */
2868 ast_channel_lock(chan);
2869 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
2870 agifile = ast_strdupa(tmpvar);
2872 agifile = ast_strdupa(agifiledefault);
2874 ast_channel_unlock(chan);
2876 if (user->dahdichannel) {
2877 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
2879 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2881 /* Find a pointer to the agi app and execute the script */
2882 agi_app = pbx_findapp("agi");
2884 ret = pbx_exec(chan, agi_app, agifile);
2886 ast_log(LOG_WARNING, "Could not find application (agi)\n");
2889 if (user->dahdichannel) {
2890 /* Remove CONFMUTE mode on DAHDI channel */
2892 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2895 if (user->dahdichannel && ast_test_flag64(confflags, CONFFLAG_STARMENU)) {
2896 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
2898 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2901 int menu_was_active = 0;
2907 if (rt_schedule && conf->endtime) {
2908 char currenttime[32];
2909 long localendtime = 0;
2912 struct ast_variable *var, *origvar;
2915 if (now.tv_sec % 60 == 0) {
2917 ast_localtime(&now, &tm, NULL);
2918 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2919 var = origvar = ast_load_realtime("meetme", "confno",
2920 conf->confno, "starttime <=", currenttime,
2921 "endtime >=", currenttime, NULL);
2923 for ( ; var; var = var->next) {
2924 if (!strcasecmp(var->name, "endtime")) {
2925 struct ast_tm endtime_tm;
2926 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
2927 tmp = ast_mktime(&endtime_tm, NULL);
2928 localendtime = tmp.tv_sec;
2931 ast_variables_destroy(origvar);
2933 /* A conference can be extended from the
2934 Admin/User menu or by an external source */
2935 if (localendtime > conf->endtime){
2936 conf->endtime = localendtime;
2940 if (conf->endtime && (now.tv_sec >= conf->endtime)) {
2941 ast_verbose("Quitting time...\n");
2945 if (!announcement_played && conf->endalert) {
2946 if (now.tv_sec + conf->endalert >= conf->endtime) {
2947 if (!ast_streamfile(chan, "conf-will-end-in", ast_channel_language(chan)))
2948 ast_waitstream(chan, "");
2949 ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", ast_channel_language(chan));
2950 if (!ast_streamfile(chan, "minutes", ast_channel_language(chan)))
2951 ast_waitstream(chan, "");
2953 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2955 announcement_played = 1;
2960 announcement_played = 0;
2970 if (user->kicktime && (user->kicktime <= now.tv_sec)) {
2971 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
2980 if (user->timelimit) {
2981 int minutes = 0, seconds = 0, remain = 0;
2983 to = ast_tvdiff_ms(nexteventts, now);
2987 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
2988 if (time_left_ms < to) {
2992 if (time_left_ms <= 0) {
2993 if (user->end_sound) {
2994 res = ast_streamfile(chan, user->end_sound, ast_channel_language(chan));
2995 res = ast_waitstream(chan, "");
2997 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
3006 if (time_left_ms >= 5000) {
3008 remain = (time_left_ms + 500) / 1000;
3009 if (remain / 60 >= 1) {
3010 minutes = remain / 60;
3011 seconds = remain % 60;
3016 /* force the time left to round up if appropriate */
3017 if (user->warning_sound && user->play_warning) {
3018 if (!strcmp(user->warning_sound, "timeleft")) {
3020 res = ast_streamfile(chan, "vm-youhave", ast_channel_language(chan));
3021 res = ast_waitstream(chan, "");
3023 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
3024 res = ast_streamfile(chan, "queue-minutes", ast_channel_language(chan));
3025 res = ast_waitstream(chan, "");
3028 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
3029 res = ast_streamfile(chan, "queue-seconds", ast_channel_language(chan));
3030 res = ast_waitstream(chan, "");
3033 res = ast_streamfile(chan, user->warning_sound, ast_channel_language(chan));
3034 res = ast_waitstream(chan, "");
3037 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3041 if (user->warning_freq) {
3042 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
3044 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
3050 if (timeout && now.tv_sec >= timeout) {
3051 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
3059 /* if we have just exited from the menu, and the user had a channel-driver
3060 volume adjustment, restore it
3062 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual) {
3063 set_talk_volume(user, user->listen.desired);
3066 menu_was_active = menu_active;
3068 currentmarked = conf->markedusers;
3069 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
3070 ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
3071 ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
3073 if (currentmarked == 1 && conf->users > 1) {
3074 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
3075 if (conf->users - 1 == 1) {
3076 if (!ast_streamfile(chan, "conf-userwilljoin", ast_channel_language(chan))) {
3077 ast_waitstream(chan, "");
3080 if (!ast_streamfile(chan, "conf-userswilljoin", ast_channel_language(chan))) {
3081 ast_waitstream(chan, "");
3085 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
3086 if (!ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan))) {
3087 ast_waitstream(chan, "");
3092 /* Update the struct with the actual confflags */
3093 user->userflags = *confflags;
3095 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
3096 if (currentmarked == 0) {
3097 if (lastmarked != 0) {
3098 if (!ast_test_flag64(confflags, CONFFLAG_QUIET)) {
3099 if (!ast_streamfile(chan, "conf-leaderhasleft", ast_channel_language(chan))) {
3100 ast_waitstream(chan, "");
3103 if (ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
3104 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
3109 dahdic.confmode = DAHDI_CONF_CONF;
3110 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3111 ast_log(LOG_WARNING, "Error setting conference\n");
3117 if (!musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
3118 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3121 } else if (currentmarked >= 1 && lastmarked == 0) {
3122 /* Marked user entered, so cancel timeout */
3124 if (ast_test_flag64(confflags, CONFFLAG_MONITOR)) {
3125 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
3126 } else if (ast_test_flag64(confflags, CONFFLAG_TALKER)) {
3127 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
3129 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
3131 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3132 ast_log(LOG_WARNING, "Error setting conference\n");
3136 if (musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
3140 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
3141 !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
3142 if (!ast_streamfile(chan, "conf-placeintoconf", ast_channel_language(chan))) {
3143 ast_waitstream(chan, "");
3145 conf_play(chan, conf, ENTER);
3150 /* trying to add moh for single person conf */
3151 if (ast_test_flag64(confflags, CONFFLAG_MOH) && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
3152 if (conf->users == 1) {
3154 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3165 /* Leave if the last marked user left */
3166 if (currentmarked == 0 && lastmarked != 0 && ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
3167 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
3175 /* Check if my modes have changed */
3177 /* If I should be muted but am still talker, mute me */
3178 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&