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>Set listen only mode (Listen only, no talking).</para>
131 <para>Set initially muted.</para>
133 <option name="M" hasparams="optional">
134 <para>Enable music on hold when the conference has a single caller. Optionally,
135 specify a musiconhold class to use. If one is not provided, it will use the
136 channel's currently set music class, or <literal>default</literal>.</para>
137 <argument name="class" required="true" />
140 <para>Set talker optimization - treats talkers who aren't speaking as
141 being muted, meaning (a) No encode is done on transmission and (b)
142 Received audio that is not registered as talking is omitted causing no
143 buildup in background noise.</para>
145 <option name="p" hasparams="optional">
146 <para>Allow user to exit the conference by pressing <literal>#</literal> (default)
147 or any of the defined keys. If keys contain <literal>*</literal> this will override
148 option <literal>s</literal>. The key used is set to channel variable
149 <variable>MEETME_EXIT_KEY</variable>.</para>
150 <argument name="keys" required="true" />
153 <para>Always prompt for the pin even if it is specified.</para>
156 <para>Quiet mode (don't play enter/leave sounds).</para>
159 <para>Record conference (records as <variable>MEETME_RECORDINGFILE</variable>
160 using format <variable>MEETME_RECORDINGFORMAT</variable>. Default filename is
161 <literal>meetme-conf-rec-${CONFNO}-${UNIQUEID}</literal> and the default format is
165 <para>Present menu (user or admin) when <literal>*</literal> is received
166 (send to menu).</para>
169 <para>Set talk only mode. (Talk only, no listening).</para>
172 <para>Set talker detection (sent to manager interface and meetme list).</para>
174 <option name="v" hasparams="optional">
175 <para>Announce when a user is joining or leaving the conference. Use the voicemail greeting as the announcement.
176 If the i or I options are set, the application will fall back to them if no voicemail greeting can be found.</para>
177 <argument name="mailbox@[context]" required="true">
178 <para>The mailbox and voicemail context to play from. If no context provided, assumed context is default.</para>
181 <option name="w" hasparams="optional">
182 <para>Wait until the marked user enters the conference.</para>
183 <argument name="secs" required="true" />
186 <para>Close the conference when last marked user exits</para>
189 <para>Allow user to exit the conference by entering a valid single digit
190 extension <variable>MEETME_EXIT_CONTEXT</variable> or the current context
191 if that variable is not defined.</para>
194 <para>Do not play message when first person enters</para>
197 <para>Kick the user <replaceable>x</replaceable> seconds <emphasis>after</emphasis> he entered into
198 the conference.</para>
199 <argument name="x" required="true" />
201 <option name="L" argsep=":">
202 <para>Limit the conference to <replaceable>x</replaceable> ms. Play a warning when
203 <replaceable>y</replaceable> ms are left. Repeat the warning every <replaceable>z</replaceable> ms.
204 The following special variables can be used with this option:</para>
206 <variable name="CONF_LIMIT_TIMEOUT_FILE">
207 <para>File to play when time is up.</para>
209 <variable name="CONF_LIMIT_WARNING_FILE">
210 <para>File to play as warning if <replaceable>y</replaceable> is defined. The
211 default is to say the time remaining.</para>
214 <argument name="x" />
215 <argument name="y" />
216 <argument name="z" />
220 <parameter name="pin" />
223 <para>Enters the user into a specified MeetMe conference. If the <replaceable>confno</replaceable>
224 is omitted, the user will be prompted to enter one. User can exit the conference by hangup, or
225 if the <literal>p</literal> option is specified, by pressing <literal>#</literal>.</para>
226 <note><para>The DAHDI kernel modules and at least one hardware driver (or dahdi_dummy)
227 must be present for conferencing to operate properly. In addition, the chan_dahdi channel driver
228 must be loaded for the <literal>i</literal> and <literal>r</literal> options to operate at
232 <ref type="application">MeetMeCount</ref>
233 <ref type="application">MeetMeAdmin</ref>
234 <ref type="application">MeetMeChannelAdmin</ref>
237 <application name="MeetMeCount" language="en_US">
239 MeetMe participant count.
242 <parameter name="confno" required="true">
243 <para>Conference number.</para>
245 <parameter name="var" />
248 <para>Plays back the number of users in the specified MeetMe conference.
249 If <replaceable>var</replaceable> is specified, playback will be skipped and the value
250 will be returned in the variable. Upon application completion, MeetMeCount will hangup
251 the channel, unless priority <literal>n+1</literal> exists, in which case priority progress will
255 <ref type="application">MeetMe</ref>
258 <application name="MeetMeAdmin" language="en_US">
260 MeetMe conference administration.
263 <parameter name="confno" required="true" />
264 <parameter name="command" required="true">
267 <para>Eject last user that joined.</para>
270 <para>Extend conference end time, if scheduled.</para>
273 <para>Kick one user out of conference.</para>
276 <para>Kick all users out of conference.</para>
279 <para>Unlock conference.</para>
282 <para>Lock conference.</para>
285 <para>Unmute one user.</para>
288 <para>Mute one user.</para>
291 <para>Unmute all users in the conference.</para>
294 <para>Mute all non-admin users in the conference.</para>
297 <para>Reset one user's volume settings.</para>
300 <para>Reset all users volume settings.</para>
303 <para>Lower entire conference speaking volume.</para>
306 <para>Raise entire conference speaking volume.</para>
309 <para>Lower one user's talk volume.</para>
312 <para>Raise one user's talk volume.</para>
315 <para>Lower one user's listen volume.</para>
318 <para>Raise one user's listen volume.</para>
321 <para>Lower entire conference listening volume.</para>
324 <para>Raise entire conference listening volume.</para>
328 <parameter name="user" />
331 <para>Run admin <replaceable>command</replaceable> for conference <replaceable>confno</replaceable>.</para>
332 <para>Will additionally set the variable <variable>MEETMEADMINSTATUS</variable> with one of
333 the following values:</para>
335 <variable name="MEETMEADMINSTATUS">
336 <value name="NOPARSE">
339 <value name="NOTFOUND">
340 User specified was not found.
342 <value name="FAILED">
343 Another failure occurred.
346 The operation was completed successfully.
352 <ref type="application">MeetMe</ref>
355 <application name="MeetMeChannelAdmin" language="en_US">
357 MeetMe conference Administration (channel specific).
360 <parameter name="channel" required="true" />
361 <parameter name="command" required="true">
364 <para>Kick the specified user out of the conference he is in.</para>
367 <para>Unmute the specified user.</para>
370 <para>Mute the specified user.</para>
376 <para>Run admin <replaceable>command</replaceable> for a specific
377 <replaceable>channel</replaceable> in any conference.</para>
380 <application name="SLAStation" language="en_US">
382 Shared Line Appearance Station.
385 <parameter name="station" required="true">
386 <para>Station name</para>
390 <para>This application should be executed by an SLA station. The argument depends
391 on how the call was initiated. If the phone was just taken off hook, then the argument
392 <replaceable>station</replaceable> should be just the station name. If the call was
393 initiated by pressing a line key, then the station name should be preceded by an underscore
394 and the trunk name associated with that line button.</para>
395 <para>For example: <literal>station1_line1</literal></para>
396 <para>On exit, this application will set the variable <variable>SLASTATION_STATUS</variable> to
397 one of the following values:</para>
399 <variable name="SLASTATION_STATUS">
400 <value name="FAILURE" />
401 <value name="CONGESTION" />
402 <value name="SUCCESS" />
407 <application name="SLATrunk" language="en_US">
409 Shared Line Appearance Trunk.
412 <parameter name="trunk" required="true">
413 <para>Trunk name</para>
415 <parameter name="options">
417 <option name="M" hasparams="optional">
418 <para>Play back the specified MOH <replaceable>class</replaceable>
419 instead of ringing</para>
420 <argument name="class" required="true" />
426 <para>This application should be executed by an SLA trunk on an inbound call. The channel calling
427 this application should correspond to the SLA trunk with the name <replaceable>trunk</replaceable>
428 that is being passed as an argument.</para>
429 <para>On exit, this application will set the variable <variable>SLATRUNK_STATUS</variable> to
430 one of the following values:</para>
432 <variable name="SLATRUNK_STATUS">
433 <value name="FAILURE" />
434 <value name="SUCCESS" />
435 <value name="UNANSWERED" />
436 <value name="RINGTIMEOUT" />
441 <function name="MEETME_INFO" language="en_US">
443 Query a given conference of various properties.
446 <parameter name="keyword" required="true">
447 <para>Options:</para>
450 <para>Boolean of whether the corresponding conference is locked.</para>
452 <enum name="parties">
453 <para>Number of parties in a given conference</para>
455 <enum name="activity">
456 <para>Duration of conference in seconds.</para>
458 <enum name="dynamic">
459 <para>Boolean of whether the corresponding conference is dynamic.</para>
463 <parameter name="confno" required="true">
464 <para>Conference number to retrieve information from.</para>
469 <ref type="application">MeetMe</ref>
470 <ref type="application">MeetMeCount</ref>
471 <ref type="application">MeetMeAdmin</ref>
472 <ref type="application">MeetMeChannelAdmin</ref>
475 <manager name="MeetmeMute" language="en_US">
480 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
481 <parameter name="Meetme" required="true" />
482 <parameter name="Usernum" required="true" />
487 <manager name="MeetmeUnmute" language="en_US">
489 Unmute a Meetme user.
492 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
493 <parameter name="Meetme" required="true" />
494 <parameter name="Usernum" required="true" />
499 <manager name="MeetmeList" language="en_US">
501 List participants in a conference.
504 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
505 <parameter name="Conference" required="false">
506 <para>Conference number.</para>
510 <para>Lists all users in a particular MeetMe conference.
511 MeetmeList will follow as separate events, followed by a final event called
512 MeetmeListComplete.</para>
515 <manager name="MeetmeListRooms" language="en_US">
517 List active conferences.
520 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
523 <para>Lists data about all active conferences.
524 MeetmeListRooms will follow as separate events, followed by a final event called
525 MeetmeListRoomsComplete.</para>
530 #define CONFIG_FILE_NAME "meetme.conf"
531 #define SLA_CONFIG_FILE "sla.conf"
533 /*! each buffer is 20ms, so this is 640ms total */
534 #define DEFAULT_AUDIO_BUFFERS 32
536 /*! String format for scheduled conferences */
537 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
540 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
541 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
542 ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */
543 /*! User has requested to speak */
544 ADMINFLAG_T_REQUEST = (1 << 4),
547 #define MEETME_DELAYDETECTTALK 300
548 #define MEETME_DELAYDETECTENDTALK 1000
550 #define AST_FRAME_BITS 32
557 enum entrance_sound {
562 enum recording_state {
564 MEETME_RECORD_STARTED,
565 MEETME_RECORD_ACTIVE,
566 MEETME_RECORD_TERMINATE
569 #define CONF_SIZE 320
572 /*! user has admin access on the conference */
573 CONFFLAG_ADMIN = (1 << 0),
574 /*! If set the user can only receive audio from the conference */
575 CONFFLAG_MONITOR = (1 << 1),
576 /*! If set asterisk will exit conference when key defined in p() option is pressed */
577 CONFFLAG_KEYEXIT = (1 << 2),
578 /*! If set asterisk will provide a menu to the user when '*' is pressed */
579 CONFFLAG_STARMENU = (1 << 3),
580 /*! If set the use can only send audio to the conference */
581 CONFFLAG_TALKER = (1 << 4),
582 /*! If set there will be no enter or leave sounds */
583 CONFFLAG_QUIET = (1 << 5),
584 /*! If set, when user joins the conference, they will be told the number
585 * of users that are already in */
586 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
587 /*! Set to run AGI Script in Background */
588 CONFFLAG_AGI = (1 << 7),
589 /*! Set to have music on hold when user is alone in conference */
590 CONFFLAG_MOH = (1 << 8),
591 /*! If set the MeetMe will return if all marked with this flag left */
592 CONFFLAG_MARKEDEXIT = (1 << 9),
593 /*! If set, the MeetMe will wait until a marked user enters */
594 CONFFLAG_WAITMARKED = (1 << 10),
595 /*! If set, the MeetMe will exit to the specified context */
596 CONFFLAG_EXIT_CONTEXT = (1 << 11),
597 /*! If set, the user will be marked */
598 CONFFLAG_MARKEDUSER = (1 << 12),
599 /*! If set, user will be ask record name on entry of conference */
600 CONFFLAG_INTROUSER = (1 << 13),
601 /*! If set, the MeetMe will be recorded */
602 CONFFLAG_RECORDCONF = (1<< 14),
603 /*! If set, the user will be monitored if the user is talking or not */
604 CONFFLAG_MONITORTALKER = (1 << 15),
605 CONFFLAG_DYNAMIC = (1 << 16),
606 CONFFLAG_DYNAMICPIN = (1 << 17),
607 CONFFLAG_EMPTY = (1 << 18),
608 CONFFLAG_EMPTYNOPIN = (1 << 19),
609 CONFFLAG_ALWAYSPROMPT = (1 << 20),
610 /*! If set, treat talking users as muted users */
611 CONFFLAG_OPTIMIZETALKER = (1 << 21),
612 /*! If set, won't speak the extra prompt when the first person
613 * enters the conference */
614 CONFFLAG_NOONLYPERSON = (1 << 22),
615 /*! If set, user will be asked to record name on entry of conference
617 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
618 /*! If set, the user will be initially self-muted */
619 CONFFLAG_STARTMUTED = (1 << 24),
620 /*! Pass DTMF through the conference */
621 CONFFLAG_PASS_DTMF = (1 << 25),
622 CONFFLAG_SLA_STATION = (1 << 26),
623 CONFFLAG_SLA_TRUNK = (1 << 27),
624 /*! If set, the user should continue in the dialplan if kicked out */
625 CONFFLAG_KICK_CONTINUE = (1 << 28),
626 CONFFLAG_DURATION_STOP = (1 << 29),
627 CONFFLAG_DURATION_LIMIT = (1 << 30),
630 /* These flags are defined separately because we ran out of bits that an enum can be used to represent.
631 If you add new flags, be sure to do it in the same way that these are. */
632 /*! Do not write any audio to this channel until the state is up. */
633 #define CONFFLAG_NO_AUDIO_UNTIL_UP (1ULL << 31)
634 #define CONFFLAG_INTROMSG (1ULL << 32) /*!< If set play an intro announcement at start of conference */
635 #define CONFFLAG_INTROUSER_VMREC (1ULL << 33)
638 OPT_ARG_WAITMARKED = 0,
639 OPT_ARG_EXITKEYS = 1,
640 OPT_ARG_DURATION_STOP = 2,
641 OPT_ARG_DURATION_LIMIT = 3,
642 OPT_ARG_MOH_CLASS = 4,
643 OPT_ARG_INTROMSG = 5,
644 OPT_ARG_INTROUSER_VMREC = 6,
645 OPT_ARG_ARRAY_SIZE = 7,
648 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
649 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
650 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
651 AST_APP_OPTION('b', CONFFLAG_AGI ),
652 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
653 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
654 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
655 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
656 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
657 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
658 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
659 AST_APP_OPTION_ARG('G', CONFFLAG_INTROMSG, OPT_ARG_INTROMSG ),
660 AST_APP_OPTION_ARG('v', CONFFLAG_INTROUSER_VMREC , OPT_ARG_INTROUSER_VMREC),
661 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
662 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
663 AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
664 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
665 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
666 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
667 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
668 AST_APP_OPTION('q', CONFFLAG_QUIET ),
669 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
670 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
671 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
672 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
673 AST_APP_OPTION('t', CONFFLAG_TALKER ),
674 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
675 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
676 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
677 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
678 AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
679 AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
682 static const char * const app = "MeetMe";
683 static const char * const app2 = "MeetMeCount";
684 static const char * const app3 = "MeetMeAdmin";
685 static const char * const app4 = "MeetMeChannelAdmin";
686 static const char * const slastation_app = "SLAStation";
687 static const char * const slatrunk_app = "SLATrunk";
689 /* Lookup RealTime conferences based on confno and current time */
690 static int rt_schedule;
691 static int fuzzystart;
692 static int earlyalert;
696 /*! Log participant count to the RealTime backend */
697 static int rt_log_members;
699 #define MAX_CONFNUM 80
701 #define OPTIONS_LEN 100
703 /* Enough space for "<conference #>,<pin>,<admin pin>" followed by a 0 byte. */
704 #define MAX_SETTINGS (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)
711 struct announce_listitem {
712 AST_LIST_ENTRY(announce_listitem) entry;
713 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
714 char language[MAX_LANGUAGE];
715 struct ast_channel *confchan;
718 enum announcetypes announcetype;
721 /*! \brief The MeetMe Conference object */
722 struct ast_conference {
723 ast_mutex_t playlock; /*!< Conference specific lock (players) */
724 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
725 char confno[MAX_CONFNUM]; /*!< Conference */
726 struct ast_channel *chan; /*!< Announcements channel */
727 struct ast_channel *lchan; /*!< Listen/Record channel */
728 int fd; /*!< Announcements fd */
729 int dahdiconf; /*!< DAHDI Conf # */
730 int users; /*!< Number of active users */
731 int markedusers; /*!< Number of marked users */
732 int maxusers; /*!< Participant limit if scheduled */
733 int endalert; /*!< When to play conf ending message */
734 time_t start; /*!< Start time (s) */
735 int refcount; /*!< reference count of usage */
736 enum recording_state recording:2; /*!< recording status */
737 unsigned int isdynamic:1; /*!< Created on the fly? */
738 unsigned int locked:1; /*!< Is the conference locked? */
739 unsigned int gmuted:1; /*!< Is the conference globally muted? (all non-admins) */
740 pthread_t recordthread; /*!< thread for recording */
741 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
742 pthread_attr_t attr; /*!< thread attribute */
743 char *recordingfilename; /*!< Filename to record the Conference into */
744 char *recordingformat; /*!< Format to record the Conference in */
745 char pin[MAX_PIN]; /*!< If protected by a PIN */
746 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
748 long endtime; /*!< When to end the conf if scheduled */
749 const char *useropts; /*!< RealTime user flags */
750 const char *adminopts; /*!< RealTime moderator flags */
751 const char *bookid; /*!< RealTime conference id */
752 struct ast_frame *transframe[32];
753 struct ast_frame *origframe;
754 struct ast_trans_pvt *transpath[32];
755 struct ao2_container *usercontainer;
756 AST_LIST_ENTRY(ast_conference) list;
757 /* announce_thread related data */
758 pthread_t announcethread;
759 ast_mutex_t announcethreadlock;
760 unsigned int announcethread_stop:1;
761 ast_cond_t announcelist_addition;
762 AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
763 ast_mutex_t announcelistlock;
766 static AST_LIST_HEAD_STATIC(confs, ast_conference);
768 static unsigned int conf_map[1024] = {0, };
771 int desired; /*!< Desired volume adjustment */
772 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
775 /*! \brief The MeetMe User object */
776 struct ast_conf_user {
777 int user_no; /*!< User Number */
778 struct ast_flags64 userflags; /*!< Flags as set in the conference */
779 int adminflags; /*!< Flags set by the Admin */
780 struct ast_channel *chan; /*!< Connected channel */
781 int talking; /*!< Is user talking */
782 int dahdichannel; /*!< Is a DAHDI channel */
783 char usrvalue[50]; /*!< Custom User Value */
784 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
785 time_t jointime; /*!< Time the user joined the conference */
786 time_t kicktime; /*!< Time the user will be kicked from the conference */
787 struct timeval start_time; /*!< Time the user entered into the conference */
788 long timelimit; /*!< Time limit for the user to be in the conference L(x:y:z) */
789 long play_warning; /*!< Play a warning when 'y' ms are left */
790 long warning_freq; /*!< Repeat the warning every 'z' ms */
791 const char *warning_sound; /*!< File to play as warning if 'y' is defined */
792 const char *end_sound; /*!< File to play when time is up. */
794 struct volume listen;
795 AST_LIST_ENTRY(ast_conf_user) list;
798 enum sla_which_trunk_refs {
803 enum sla_trunk_state {
804 SLA_TRUNK_STATE_IDLE,
805 SLA_TRUNK_STATE_RINGING,
807 SLA_TRUNK_STATE_ONHOLD,
808 SLA_TRUNK_STATE_ONHOLD_BYME,
811 enum sla_hold_access {
812 /*! This means that any station can put it on hold, and any station
813 * can retrieve the call from hold. */
815 /*! This means that only the station that put the call on hold may
816 * retrieve it from hold. */
820 struct sla_trunk_ref;
823 AST_RWLIST_ENTRY(sla_station) entry;
824 AST_DECLARE_STRING_FIELDS(
825 AST_STRING_FIELD(name);
826 AST_STRING_FIELD(device);
827 AST_STRING_FIELD(autocontext);
829 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
830 struct ast_dial *dial;
831 /*! Ring timeout for this station, for any trunk. If a ring timeout
832 * is set for a specific trunk on this station, that will take
833 * priority over this value. */
834 unsigned int ring_timeout;
835 /*! Ring delay for this station, for any trunk. If a ring delay
836 * is set for a specific trunk on this station, that will take
837 * priority over this value. */
838 unsigned int ring_delay;
839 /*! This option uses the values in the sla_hold_access enum and sets the
840 * access control type for hold on this station. */
841 unsigned int hold_access:1;
842 /*! Use count for inside sla_station_exec */
843 unsigned int ref_count;
846 struct sla_station_ref {
847 AST_LIST_ENTRY(sla_station_ref) entry;
848 struct sla_station *station;
852 AST_RWLIST_ENTRY(sla_trunk) entry;
853 AST_DECLARE_STRING_FIELDS(
854 AST_STRING_FIELD(name);
855 AST_STRING_FIELD(device);
856 AST_STRING_FIELD(autocontext);
858 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
859 /*! Number of stations that use this trunk */
860 unsigned int num_stations;
861 /*! Number of stations currently on a call with this trunk */
862 unsigned int active_stations;
863 /*! Number of stations that have this trunk on hold. */
864 unsigned int hold_stations;
865 struct ast_channel *chan;
866 unsigned int ring_timeout;
867 /*! If set to 1, no station will be able to join an active call with
869 unsigned int barge_disabled:1;
870 /*! This option uses the values in the sla_hold_access enum and sets the
871 * access control type for hold on this trunk. */
872 unsigned int hold_access:1;
873 /*! Whether this trunk is currently on hold, meaning that once a station
874 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
875 unsigned int on_hold:1;
876 /*! Use count for inside sla_trunk_exec */
877 unsigned int ref_count;
880 struct sla_trunk_ref {
881 AST_LIST_ENTRY(sla_trunk_ref) entry;
882 struct sla_trunk *trunk;
883 enum sla_trunk_state state;
884 struct ast_channel *chan;
885 /*! Ring timeout to use when this trunk is ringing on this specific
886 * station. This takes higher priority than a ring timeout set at
887 * the station level. */
888 unsigned int ring_timeout;
889 /*! Ring delay to use when this trunk is ringing on this specific
890 * station. This takes higher priority than a ring delay set at
891 * the station level. */
892 unsigned int ring_delay;
895 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
896 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
898 static const char sla_registrar[] = "SLA";
900 /*! \brief Event types that can be queued up for the SLA thread */
901 enum sla_event_type {
902 /*! A station has put the call on hold */
904 /*! The state of a dial has changed */
905 SLA_EVENT_DIAL_STATE,
906 /*! The state of a ringing trunk has changed */
907 SLA_EVENT_RINGING_TRUNK,
908 /*! A reload of configuration has been requested */
910 /*! Poke the SLA thread so it can check if it can perform a reload */
911 SLA_EVENT_CHECK_RELOAD,
915 enum sla_event_type type;
916 struct sla_station *station;
917 struct sla_trunk_ref *trunk_ref;
918 AST_LIST_ENTRY(sla_event) entry;
921 /*! \brief A station that failed to be dialed
922 * \note Only used by the SLA thread. */
923 struct sla_failed_station {
924 struct sla_station *station;
925 struct timeval last_try;
926 AST_LIST_ENTRY(sla_failed_station) entry;
929 /*! \brief A trunk that is ringing */
930 struct sla_ringing_trunk {
931 struct sla_trunk *trunk;
932 /*! The time that this trunk started ringing */
933 struct timeval ring_begin;
934 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
935 AST_LIST_ENTRY(sla_ringing_trunk) entry;
938 enum sla_station_hangup {
939 SLA_STATION_HANGUP_NORMAL,
940 SLA_STATION_HANGUP_TIMEOUT,
943 /*! \brief A station that is ringing */
944 struct sla_ringing_station {
945 struct sla_station *station;
946 /*! The time that this station started ringing */
947 struct timeval ring_begin;
948 AST_LIST_ENTRY(sla_ringing_station) entry;
952 * \brief A structure for data used by the sla thread
955 /*! The SLA thread ID */
959 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
960 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
961 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
962 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
964 /*! Attempt to handle CallerID, even though it is known not to work
965 * properly in some situations. */
966 unsigned int attempt_callerid:1;
967 /*! A reload has been requested */
968 unsigned int reload:1;
970 .thread = AST_PTHREADT_NULL,
973 /*! \brief The number of audio buffers to be allocated on pseudo channels
974 * when in a conference */
975 static int audio_buffers;
977 /*! \brief Map 'volume' levels from -5 through +5 into decibel (dB)
978 * settings for channel drivers.
980 * \note these are not a straight linear-to-dB
981 * conversion... the numbers have been modified
982 * to give the user a better level of adjustability.
984 static const char gain_map[] = {
999 static int admin_exec(struct ast_channel *chan, const char *data);
1000 static void *recordthread(void *args);
1002 static const char *istalking(int x)
1007 return "(unmonitored)";
1009 return "(not talking)";
1012 static int careful_write(int fd, unsigned char *data, int len, int block)
1019 x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
1020 res = ioctl(fd, DAHDI_IOMUX, &x);
1024 res = write(fd, data, len);
1026 if (errno != EAGAIN) {
1027 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
1039 static int set_talk_volume(struct ast_conf_user *user, int volume)
1043 /* attempt to make the adjustment in the channel driver;
1044 if successful, don't adjust in the frame reading routine
1046 gain_adjust = gain_map[volume + 5];
1048 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1051 static int set_listen_volume(struct ast_conf_user *user, int volume)
1055 /* attempt to make the adjustment in the channel driver;
1056 if successful, don't adjust in the frame reading routine
1058 gain_adjust = gain_map[volume + 5];
1060 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1063 static void tweak_volume(struct volume *vol, enum volume_action action)
1067 switch (vol->desired) {
1082 switch (vol->desired) {
1098 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
1100 tweak_volume(&user->talk, action);
1101 /* attempt to make the adjustment in the channel driver;
1102 if successful, don't adjust in the frame reading routine
1104 if (!set_talk_volume(user, user->talk.desired))
1105 user->talk.actual = 0;
1107 user->talk.actual = user->talk.desired;
1110 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
1112 tweak_volume(&user->listen, action);
1113 /* attempt to make the adjustment in the channel driver;
1114 if successful, don't adjust in the frame reading routine
1116 if (!set_listen_volume(user, user->listen.desired))
1117 user->listen.actual = 0;
1119 user->listen.actual = user->listen.desired;
1122 static void reset_volumes(struct ast_conf_user *user)
1124 signed char zero_volume = 0;
1126 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
1127 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
1130 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
1132 unsigned char *data;
1136 if (!ast_check_hangup(chan))
1137 res = ast_autoservice_start(chan);
1139 AST_LIST_LOCK(&confs);
1144 len = sizeof(enter);
1148 len = sizeof(leave);
1155 careful_write(conf->fd, data, len, 1);
1158 AST_LIST_UNLOCK(&confs);
1161 ast_autoservice_stop(chan);
1164 static int user_no_cmp(void *obj, void *arg, int flags)
1166 struct ast_conf_user *user = obj;
1169 if (user->user_no == *user_no) {
1170 return (CMP_MATCH | CMP_STOP);
1176 static int user_max_cmp(void *obj, void *arg, int flags)
1178 struct ast_conf_user *user = obj;
1181 if (user->user_no > *max_no) {
1182 *max_no = user->user_no;
1189 * \brief Find or create a conference
1191 * \param confno The conference name/number
1192 * \param pin The regular user pin
1193 * \param pinadmin The admin pin
1194 * \param make Make the conf if it doesn't exist
1195 * \param dynamic Mark the newly created conference as dynamic
1196 * \param refcount How many references to mark on the conference
1197 * \param chan The asterisk channel
1199 * \return A pointer to the conference struct, or NULL if it wasn't found and
1200 * make or dynamic were not set.
1202 static struct ast_conference *build_conf(const char *confno, const char *pin,
1203 const char *pinadmin, int make, int dynamic, int refcount,
1204 const struct ast_channel *chan, struct ast_test *test)
1206 struct ast_conference *cnf;
1207 struct dahdi_confinfo dahdic = { 0, };
1209 struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
1210 struct ast_format tmp_fmt;
1212 AST_LIST_LOCK(&confs);
1214 AST_LIST_TRAVERSE(&confs, cnf, list) {
1215 if (!strcmp(confno, cnf->confno))
1219 if (cnf || (!make && !dynamic) || !cap_slin)
1222 ast_format_cap_add(cap_slin, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0));
1223 /* Make a new one */
1224 if (!(cnf = ast_calloc(1, sizeof(*cnf))) ||
1225 !(cnf->usercontainer = ao2_container_alloc(1, NULL, user_no_cmp))) {
1229 ast_mutex_init(&cnf->playlock);
1230 ast_mutex_init(&cnf->listenlock);
1231 cnf->recordthread = AST_PTHREADT_NULL;
1232 ast_mutex_init(&cnf->recordthreadlock);
1233 cnf->announcethread = AST_PTHREADT_NULL;
1234 ast_mutex_init(&cnf->announcethreadlock);
1235 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
1236 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
1237 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
1238 ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
1240 /* Setup a new dahdi conference */
1242 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1243 cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
1244 if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
1246 /* if we are creating a conference for a unit test, it is not neccesary
1247 * to open a pseudo channel, so, if we fail continue creating
1248 * the conference. */
1249 ast_test_status_update(test, "Unable to open DAHDI pseudo device\n");
1251 ast_log(LOG_WARNING, "Unable to open DAHDI pseudo device\n");
1254 ao2_ref(cnf->usercontainer, -1);
1255 ast_mutex_destroy(&cnf->playlock);
1256 ast_mutex_destroy(&cnf->listenlock);
1257 ast_mutex_destroy(&cnf->recordthreadlock);
1258 ast_mutex_destroy(&cnf->announcethreadlock);
1265 cnf->dahdiconf = dahdic.confno;
1267 /* Setup a new channel for playback of audio files */
1268 cnf->chan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL);
1270 ast_set_read_format_by_id(cnf->chan, AST_FORMAT_SLINEAR);
1271 ast_set_write_format_by_id(cnf->chan, AST_FORMAT_SLINEAR);
1273 dahdic.confno = cnf->dahdiconf;
1274 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1275 if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &dahdic)) {
1277 ast_test_status_update(test, "Error setting conference on pseudo channel\n");
1279 ast_log(LOG_WARNING, "Error setting conference\n");
1281 ast_hangup(cnf->chan);
1284 ao2_ref(cnf->usercontainer, -1);
1285 ast_mutex_destroy(&cnf->playlock);
1286 ast_mutex_destroy(&cnf->listenlock);
1287 ast_mutex_destroy(&cnf->recordthreadlock);
1288 ast_mutex_destroy(&cnf->announcethreadlock);
1295 /* Fill the conference struct */
1296 cnf->start = time(NULL);
1297 cnf->maxusers = 0x7fffffff;
1298 cnf->isdynamic = dynamic ? 1 : 0;
1299 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
1300 AST_LIST_INSERT_HEAD(&confs, cnf, list);
1302 /* Reserve conference number in map */
1303 if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1304 conf_map[confno_int] = 1;
1307 cap_slin = ast_format_cap_destroy(cap_slin);
1309 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
1311 AST_LIST_UNLOCK(&confs);
1316 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
1318 static const char * const cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
1320 int len = strlen(word);
1322 struct ast_conference *cnf = NULL;
1323 struct ast_conf_user *usr = NULL;
1324 char *confno = NULL;
1325 char usrno[50] = "";
1326 char *myline, *ret = NULL;
1328 if (pos == 1) { /* Command */
1329 return ast_cli_complete(word, cmds, state);
1330 } else if (pos == 2) { /* Conference Number */
1331 AST_LIST_LOCK(&confs);
1332 AST_LIST_TRAVERSE(&confs, cnf, list) {
1333 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
1338 ret = ast_strdup(ret); /* dup before releasing the lock */
1339 AST_LIST_UNLOCK(&confs);
1341 } else if (pos == 3) {
1342 /* User Number || Conf Command option*/
1343 if (strstr(line, "mute") || strstr(line, "kick")) {
1344 if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
1345 return ast_strdup("all");
1347 AST_LIST_LOCK(&confs);
1349 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
1350 myline = ast_strdupa(line);
1351 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
1352 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
1356 AST_LIST_TRAVERSE(&confs, cnf, list) {
1357 if (!strcmp(confno, cnf->confno))
1362 struct ao2_iterator user_iter;
1363 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
1365 while((usr = ao2_iterator_next(&user_iter))) {
1366 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
1367 if (!strncasecmp(word, usrno, len) && ++which > state) {
1373 ao2_iterator_destroy(&user_iter);
1374 AST_LIST_UNLOCK(&confs);
1375 return usr ? ast_strdup(usrno) : NULL;
1377 AST_LIST_UNLOCK(&confs);
1384 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1386 /* Process the command */
1387 struct ast_conf_user *user;
1388 struct ast_conference *cnf;
1390 int i = 0, total = 0;
1392 struct ast_str *cmdline = NULL;
1393 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
1394 #define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
1398 e->command = "meetme list [concise]";
1400 "Usage: meetme list [concise] <confno> \n"
1401 " List all or a specific conference.\n";
1404 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1407 /* Check for length so no buffer will overflow... */
1408 for (i = 0; i < a->argc; i++) {
1409 if (strlen(a->argv[i]) > 100)
1410 ast_cli(a->fd, "Invalid Arguments.\n");
1413 /* Max confno length */
1414 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1418 if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], "concise"))) {
1419 /* List all the conferences */
1420 int concise = (a->argc == 3 && !strcasecmp(a->argv[2], "concise"));
1422 AST_LIST_LOCK(&confs);
1423 if (AST_LIST_EMPTY(&confs)) {
1425 ast_cli(a->fd, "No active MeetMe conferences.\n");
1427 AST_LIST_UNLOCK(&confs);
1432 ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
1434 AST_LIST_TRAVERSE(&confs, cnf, list) {
1435 if (cnf->markedusers == 0) {
1436 ast_str_set(&cmdline, 0, "N/A ");
1438 ast_str_set(&cmdline, 0, "%4.4d", cnf->markedusers);
1440 hr = (now - cnf->start) / 3600;
1441 min = ((now - cnf->start) % 3600) / 60;
1442 sec = (now - cnf->start) % 60;
1444 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");
1446 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1455 total += cnf->users;
1457 AST_LIST_UNLOCK(&confs);
1459 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1463 } else if (strcmp(a->argv[1], "list") == 0) {
1464 struct ao2_iterator user_iter;
1465 int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
1466 /* List all the users in a conference */
1467 if (AST_LIST_EMPTY(&confs)) {
1469 ast_cli(a->fd, "No active MeetMe conferences.\n");
1474 /* Find the right conference */
1475 AST_LIST_LOCK(&confs);
1476 AST_LIST_TRAVERSE(&confs, cnf, list) {
1477 if (strcmp(cnf->confno, a->argv[2]) == 0) {
1483 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
1484 AST_LIST_UNLOCK(&confs);
1488 /* Show all the users */
1490 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
1491 while((user = ao2_iterator_next(&user_iter))) {
1492 hr = (now - user->jointime) / 3600;
1493 min = ((now - user->jointime) % 3600) / 60;
1494 sec = (now - user->jointime) % 60;
1496 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1498 S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
1499 S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<no name>"),
1501 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "(Admin)" : "",
1502 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "(Listen only)" : "",
1503 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1504 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1505 istalking(user->talking), hr, min, sec);
1507 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1509 S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, ""),
1510 S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, ""),
1512 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "1" : "",
1513 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "1" : "",
1514 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1515 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1516 user->talking, hr, min, sec);
1520 ao2_iterator_destroy(&user_iter);
1522 ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
1524 AST_LIST_UNLOCK(&confs);
1530 return CLI_SHOWUSAGE;
1533 ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
1535 admin_exec(NULL, ast_str_buffer(cmdline));
1542 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1544 /* Process the command */
1545 struct ast_str *cmdline = NULL;
1550 e->command = "meetme {lock|unlock|mute|unmute|kick}";
1552 "Usage: meetme (un)lock|(un)mute|kick <confno> <usernumber>\n"
1553 " Executes a command for the conference or on a conferee\n";
1556 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1560 ast_cli(a->fd, "Invalid Arguments.\n");
1561 /* Check for length so no buffer will overflow... */
1562 for (i = 0; i < a->argc; i++) {
1563 if (strlen(a->argv[i]) > 100)
1564 ast_cli(a->fd, "Invalid Arguments.\n");
1567 /* Max confno length */
1568 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1574 return CLI_SHOWUSAGE;
1577 ast_str_set(&cmdline, 0, "%s", a->argv[2]); /* Argv 2: conference number */
1578 if (strstr(a->argv[1], "lock")) {
1579 if (strcmp(a->argv[1], "lock") == 0) {
1581 ast_str_append(&cmdline, 0, ",L");
1584 ast_str_append(&cmdline, 0, ",l");
1586 } else if (strstr(a->argv[1], "mute")) {
1589 return CLI_SHOWUSAGE;
1591 if (strcmp(a->argv[1], "mute") == 0) {
1593 if (strcmp(a->argv[3], "all") == 0) {
1594 ast_str_append(&cmdline, 0, ",N");
1596 ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);
1600 if (strcmp(a->argv[3], "all") == 0) {
1601 ast_str_append(&cmdline, 0, ",n");
1603 ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
1606 } else if (strcmp(a->argv[1], "kick") == 0) {
1609 return CLI_SHOWUSAGE;
1611 if (strcmp(a->argv[3], "all") == 0) {
1613 ast_str_append(&cmdline, 0, ",K");
1615 /* Kick a single user */
1616 ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
1620 return CLI_SHOWUSAGE;
1623 ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
1625 admin_exec(NULL, ast_str_buffer(cmdline));
1631 static const char *sla_hold_str(unsigned int hold_access)
1633 const char *hold = "Unknown";
1635 switch (hold_access) {
1639 case SLA_HOLD_PRIVATE:
1648 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1650 const struct sla_trunk *trunk;
1654 e->command = "sla show trunks";
1656 "Usage: sla show trunks\n"
1657 " This will list all trunks defined in sla.conf\n";
1664 "=============================================================\n"
1665 "=== Configured SLA Trunks ===================================\n"
1666 "=============================================================\n"
1668 AST_RWLIST_RDLOCK(&sla_trunks);
1669 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1670 struct sla_station_ref *station_ref;
1671 char ring_timeout[16] = "(none)";
1672 if (trunk->ring_timeout)
1673 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1674 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1675 "=== Trunk Name: %s\n"
1676 "=== ==> Device: %s\n"
1677 "=== ==> AutoContext: %s\n"
1678 "=== ==> RingTimeout: %s\n"
1679 "=== ==> BargeAllowed: %s\n"
1680 "=== ==> HoldAccess: %s\n"
1681 "=== ==> Stations ...\n",
1682 trunk->name, trunk->device,
1683 S_OR(trunk->autocontext, "(none)"),
1685 trunk->barge_disabled ? "No" : "Yes",
1686 sla_hold_str(trunk->hold_access));
1687 AST_RWLIST_RDLOCK(&sla_stations);
1688 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1689 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
1690 AST_RWLIST_UNLOCK(&sla_stations);
1691 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
1693 AST_RWLIST_UNLOCK(&sla_trunks);
1694 ast_cli(a->fd, "=============================================================\n\n");
1699 static const char *trunkstate2str(enum sla_trunk_state state)
1701 #define S(e) case e: return # e;
1703 S(SLA_TRUNK_STATE_IDLE)
1704 S(SLA_TRUNK_STATE_RINGING)
1705 S(SLA_TRUNK_STATE_UP)
1706 S(SLA_TRUNK_STATE_ONHOLD)
1707 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1709 return "Uknown State";
1713 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1715 const struct sla_station *station;
1719 e->command = "sla show stations";
1721 "Usage: sla show stations\n"
1722 " This will list all stations defined in sla.conf\n";
1729 "=============================================================\n"
1730 "=== Configured SLA Stations =================================\n"
1731 "=============================================================\n"
1733 AST_RWLIST_RDLOCK(&sla_stations);
1734 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1735 struct sla_trunk_ref *trunk_ref;
1736 char ring_timeout[16] = "(none)";
1737 char ring_delay[16] = "(none)";
1738 if (station->ring_timeout) {
1739 snprintf(ring_timeout, sizeof(ring_timeout),
1740 "%u", station->ring_timeout);
1742 if (station->ring_delay) {
1743 snprintf(ring_delay, sizeof(ring_delay),
1744 "%u", station->ring_delay);
1746 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1747 "=== Station Name: %s\n"
1748 "=== ==> Device: %s\n"
1749 "=== ==> AutoContext: %s\n"
1750 "=== ==> RingTimeout: %s\n"
1751 "=== ==> RingDelay: %s\n"
1752 "=== ==> HoldAccess: %s\n"
1753 "=== ==> Trunks ...\n",
1754 station->name, station->device,
1755 S_OR(station->autocontext, "(none)"),
1756 ring_timeout, ring_delay,
1757 sla_hold_str(station->hold_access));
1758 AST_RWLIST_RDLOCK(&sla_trunks);
1759 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1760 if (trunk_ref->ring_timeout) {
1761 snprintf(ring_timeout, sizeof(ring_timeout),
1762 "%u", trunk_ref->ring_timeout);
1764 strcpy(ring_timeout, "(none)");
1765 if (trunk_ref->ring_delay) {
1766 snprintf(ring_delay, sizeof(ring_delay),
1767 "%u", trunk_ref->ring_delay);
1769 strcpy(ring_delay, "(none)");
1770 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
1771 "=== ==> State: %s\n"
1772 "=== ==> RingTimeout: %s\n"
1773 "=== ==> RingDelay: %s\n",
1774 trunk_ref->trunk->name,
1775 trunkstate2str(trunk_ref->state),
1776 ring_timeout, ring_delay);
1778 AST_RWLIST_UNLOCK(&sla_trunks);
1779 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1782 AST_RWLIST_UNLOCK(&sla_stations);
1783 ast_cli(a->fd, "============================================================\n"
1789 static struct ast_cli_entry cli_meetme[] = {
1790 AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
1791 AST_CLI_DEFINE(meetme_show_cmd, "List all or one conference"),
1792 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
1793 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
1796 static void conf_flush(int fd, struct ast_channel *chan)
1800 /* read any frames that may be waiting on the channel
1804 struct ast_frame *f;
1806 /* when no frames are available, this will wait
1807 for 1 millisecond maximum
1809 while (ast_waitfor(chan, 1)) {
1813 else /* channel was hung up or something else happened */
1818 /* flush any data sitting in the pseudo channel */
1819 x = DAHDI_FLUSH_ALL;
1820 if (ioctl(fd, DAHDI_FLUSH, &x))
1821 ast_log(LOG_WARNING, "Error flushing channel\n");
1825 /*! \brief Remove the conference from the list and free it.
1827 We assume that this was called while holding conflock. */
1828 static int conf_free(struct ast_conference *conf)
1831 struct announce_listitem *item;
1833 AST_LIST_REMOVE(&confs, conf, list);
1834 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1836 if (conf->recording == MEETME_RECORD_ACTIVE) {
1837 conf->recording = MEETME_RECORD_TERMINATE;
1838 AST_LIST_UNLOCK(&confs);
1841 AST_LIST_LOCK(&confs);
1842 if (conf->recording == MEETME_RECORD_OFF)
1844 AST_LIST_UNLOCK(&confs);
1848 for (x = 0; x < AST_FRAME_BITS; x++) {
1849 if (conf->transframe[x])
1850 ast_frfree(conf->transframe[x]);
1851 if (conf->transpath[x])
1852 ast_translator_free_path(conf->transpath[x]);
1854 if (conf->announcethread != AST_PTHREADT_NULL) {
1855 ast_mutex_lock(&conf->announcelistlock);
1856 conf->announcethread_stop = 1;
1857 ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
1858 ast_cond_signal(&conf->announcelist_addition);
1859 ast_mutex_unlock(&conf->announcelistlock);
1860 pthread_join(conf->announcethread, NULL);
1862 while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
1863 /* If it's a voicemail greeting file we don't want to remove it */
1865 ast_filedelete(item->namerecloc, NULL);
1869 ast_mutex_destroy(&conf->announcelistlock);
1872 if (conf->origframe)
1873 ast_frfree(conf->origframe);
1875 ast_hangup(conf->lchan);
1877 ast_hangup(conf->chan);
1880 if (conf->recordingfilename) {
1881 ast_free(conf->recordingfilename);
1883 if (conf->usercontainer) {
1884 ao2_ref(conf->usercontainer, -1);
1886 if (conf->recordingformat) {
1887 ast_free(conf->recordingformat);
1889 ast_mutex_destroy(&conf->playlock);
1890 ast_mutex_destroy(&conf->listenlock);
1891 ast_mutex_destroy(&conf->recordthreadlock);
1892 ast_mutex_destroy(&conf->announcethreadlock);
1898 static void conf_queue_dtmf(const struct ast_conference *conf,
1899 const struct ast_conf_user *sender, struct ast_frame *f)
1901 struct ast_conf_user *user;
1902 struct ao2_iterator user_iter;
1904 user_iter = ao2_iterator_init(conf->usercontainer, 0);
1905 while ((user = ao2_iterator_next(&user_iter))) {
1906 if (user == sender) {
1910 if (ast_write(user->chan, f) < 0)
1911 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1914 ao2_iterator_destroy(&user_iter);
1917 static void sla_queue_event_full(enum sla_event_type type,
1918 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1920 struct sla_event *event;
1922 if (sla.thread == AST_PTHREADT_NULL) {
1926 if (!(event = ast_calloc(1, sizeof(*event))))
1930 event->trunk_ref = trunk_ref;
1931 event->station = station;
1934 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1938 ast_mutex_lock(&sla.lock);
1939 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1940 ast_cond_signal(&sla.cond);
1941 ast_mutex_unlock(&sla.lock);
1944 static void sla_queue_event_nolock(enum sla_event_type type)
1946 sla_queue_event_full(type, NULL, NULL, 0);
1949 static void sla_queue_event(enum sla_event_type type)
1951 sla_queue_event_full(type, NULL, NULL, 1);
1954 /*! \brief Queue a SLA event from the conference */
1955 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1956 struct ast_conference *conf)
1958 struct sla_station *station;
1959 struct sla_trunk_ref *trunk_ref = NULL;
1962 trunk_name = ast_strdupa(conf->confno);
1963 strsep(&trunk_name, "_");
1964 if (ast_strlen_zero(trunk_name)) {
1965 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1969 AST_RWLIST_RDLOCK(&sla_stations);
1970 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1971 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1972 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1978 AST_RWLIST_UNLOCK(&sla_stations);
1981 ast_debug(1, "Trunk not found for event!\n");
1985 sla_queue_event_full(type, trunk_ref, station, 1);
1988 /*! \brief Decrement reference counts, as incremented by find_conf() */
1989 static int dispose_conf(struct ast_conference *conf)
1994 AST_LIST_LOCK(&confs);
1995 if (ast_atomic_dec_and_test(&conf->refcount)) {
1996 /* Take the conference room number out of an inuse state */
1997 if ((sscanf(conf->confno, "%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
1998 conf_map[confno_int] = 0;
2003 AST_LIST_UNLOCK(&confs);
2008 static int rt_extend_conf(const char *confno)
2010 char currenttime[32];
2014 struct ast_variable *var, *orig_var;
2023 ast_localtime(&now, &tm, NULL);
2024 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2026 var = ast_load_realtime("meetme", "confno",
2027 confno, "startTime<= ", currenttime,
2028 "endtime>= ", currenttime, NULL);
2032 /* Identify the specific RealTime conference */
2034 if (!strcasecmp(var->name, "bookid")) {
2035 ast_copy_string(bookid, var->value, sizeof(bookid));
2037 if (!strcasecmp(var->name, "endtime")) {
2038 ast_copy_string(endtime, var->value, sizeof(endtime));
2043 ast_variables_destroy(orig_var);
2045 ast_strptime(endtime, DATE_FORMAT, &tm);
2046 now = ast_mktime(&tm, NULL);
2048 now.tv_sec += extendby;
2050 ast_localtime(&now, &tm, NULL);
2051 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2052 strcat(currenttime, "0"); /* Seconds needs to be 00 */
2054 var = ast_load_realtime("meetme", "confno",
2055 confno, "startTime<= ", currenttime,
2056 "endtime>= ", currenttime, NULL);
2058 /* If there is no conflict with extending the conference, update the DB */
2060 ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
2061 ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
2066 ast_variables_destroy(var);
2070 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
2074 ast_channel_lock(chan);
2075 original_moh = ast_strdupa(chan->musicclass);
2076 ast_string_field_set(chan, musicclass, musicclass);
2077 ast_channel_unlock(chan);
2079 ast_moh_start(chan, original_moh, NULL);
2081 ast_channel_lock(chan);
2082 ast_string_field_set(chan, musicclass, original_moh);
2083 ast_channel_unlock(chan);
2086 static const char *get_announce_filename(enum announcetypes type)
2090 return "conf-hasleft";
2093 return "conf-hasjoin";
2100 static void *announce_thread(void *data)
2102 struct announce_listitem *current;
2103 struct ast_conference *conf = data;
2105 char filename[PATH_MAX] = "";
2106 AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
2107 AST_LIST_HEAD_INIT_NOLOCK(&local_list);
2109 while (!conf->announcethread_stop) {
2110 ast_mutex_lock(&conf->announcelistlock);
2111 if (conf->announcethread_stop) {
2112 ast_mutex_unlock(&conf->announcelistlock);
2115 if (AST_LIST_EMPTY(&conf->announcelist))
2116 ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
2118 AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
2119 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2121 ast_mutex_unlock(&conf->announcelistlock);
2122 if (conf->announcethread_stop) {
2126 for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
2127 ast_debug(1, "About to play %s\n", current->namerecloc);
2128 if (!ast_fileexists(current->namerecloc, NULL, NULL))
2130 if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
2131 if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
2132 res = ast_waitstream(current->confchan, "");
2134 ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
2135 if (!ast_streamfile(current->confchan, filename, current->language))
2136 ast_waitstream(current->confchan, "");
2139 if (current->announcetype == CONF_HASLEFT && current->announcetype && !current->vmrec) {
2140 /* only remove it if it isn't a VM recording file */
2141 ast_filedelete(current->namerecloc, NULL);
2146 /* thread marked to stop, clean up */
2147 while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
2148 /* only delete if it's a vm rec */
2149 if (!current->vmrec) {
2150 ast_filedelete(current->namerecloc, NULL);
2152 ao2_ref(current, -1);
2157 static int can_write(struct ast_channel *chan, struct ast_flags64 *confflags)
2159 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
2163 return (chan->_state == AST_STATE_UP);
2166 static void send_talking_event(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking)
2168 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalking",
2174 chan->name, chan->uniqueid, conf->confno, user->user_no, talking ? "on" : "off");
2177 static void set_user_talking(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor)
2179 int last_talking = user->talking;
2180 if (last_talking == talking)
2183 user->talking = talking;
2186 /* Check if talking state changed. Take care of -1 which means unmonitored */
2187 int was_talking = (last_talking > 0);
2188 int now_talking = (talking > 0);
2189 if (was_talking != now_talking) {
2190 send_talking_event(chan, conf, user, now_talking);
2195 static int user_set_kickme_cb(void *obj, void *check_admin_arg, int flags)
2197 struct ast_conf_user *user = obj;
2198 /* actual pointer contents of check_admin_arg is irrelevant */
2200 if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
2201 user->adminflags |= ADMINFLAG_KICKME;
2206 static int user_set_unmuted_cb(void *obj, void *check_admin_arg, int flags)
2208 struct ast_conf_user *user = obj;
2209 /* actual pointer contents of check_admin_arg is irrelevant */
2211 if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
2212 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
2217 static int user_set_muted_cb(void *obj, void *check_admin_arg, int flags)
2219 struct ast_conf_user *user = obj;
2220 /* actual pointer contents of check_admin_arg is irrelevant */
2222 if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
2223 user->adminflags |= ADMINFLAG_MUTED;
2228 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struct ast_flags64 *confflags, char *optargs[])
2230 struct ast_conf_user *user = NULL;
2232 struct dahdi_confinfo dahdic, dahdic_empty;
2233 struct ast_frame *f;
2234 struct ast_channel *c;
2235 struct ast_frame fr;
2242 int musiconhold = 0, mohtempstopped = 0;
2245 int currentmarked = 0;
2248 int menu_active = 0;
2249 int menu8_active = 0;
2250 int talkreq_manager = 0;
2251 int using_pseudo = 0;
2255 int announcement_played = 0;
2257 struct ast_dsp *dsp = NULL;
2258 struct ast_app *agi_app;
2259 char *agifile, *mod_speex;
2260 const char *agifiledefault = "conf-background.agi", *tmpvar;
2261 char meetmesecs[30] = "";
2262 char exitcontext[AST_MAX_CONTEXT] = "";
2263 char recordingtmp[AST_MAX_EXTENSION] = "";
2264 char members[10] = "";
2265 int dtmf, opt_waitmarked_timeout = 0;
2267 struct dahdi_bufferinfo bi;
2268 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
2269 char *buf = __buf + AST_FRIENDLY_OFFSET;
2270 char *exitkeys = NULL;
2271 unsigned int calldurationlimit = 0;
2273 long play_warning = 0;
2274 long warning_freq = 0;
2275 const char *warning_sound = NULL;
2276 const char *end_sound = NULL;
2278 long time_left_ms = 0;
2279 struct timeval nexteventts = { 0, };
2281 int setusercount = 0;
2282 int confsilence = 0, totalsilence = 0;
2283 char *mailbox, *context;
2284 struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
2285 struct ast_format tmpfmt;
2288 goto conf_run_cleanup;
2290 ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
2292 if (!(user = ao2_alloc(sizeof(*user), NULL))) {
2293 goto conf_run_cleanup;
2296 /* Possible timeout waiting for marked user */
2297 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
2298 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
2299 (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
2300 (opt_waitmarked_timeout > 0)) {
2301 timeout = time(NULL) + opt_waitmarked_timeout;
2304 if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
2305 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
2306 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
2309 if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
2310 char *limit_str, *warning_str, *warnfreq_str;
2313 parse = optargs[OPT_ARG_DURATION_LIMIT];
2314 limit_str = strsep(&parse, ":");
2315 warning_str = strsep(&parse, ":");
2316 warnfreq_str = parse;
2318 timelimit = atol(limit_str);
2320 play_warning = atol(warning_str);
2322 warning_freq = atol(warnfreq_str);
2325 timelimit = play_warning = warning_freq = 0;
2326 warning_sound = NULL;
2327 } else if (play_warning > timelimit) {
2328 if (!warning_freq) {
2331 while (play_warning > timelimit)
2332 play_warning -= warning_freq;
2333 if (play_warning < 1)
2334 play_warning = warning_freq = 0;
2338 ast_verb(3, "Setting conference duration limit to: %ldms.\n", timelimit);
2340 ast_verb(3, "Setting warning time to %ldms from the conference duration limit.\n", play_warning);
2343 ast_verb(3, "Setting warning frequency to %ldms.\n", warning_freq);
2346 ast_channel_lock(chan);
2347 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
2348 var = ast_strdupa(var);
2350 ast_channel_unlock(chan);
2352 warning_sound = var ? var : "timeleft";
2354 ast_channel_lock(chan);
2355 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
2356 var = ast_strdupa(var);
2358 ast_channel_unlock(chan);
2360 end_sound = var ? var : NULL;
2362 /* undo effect of S(x) in case they are both used */
2363 calldurationlimit = 0;
2364 /* more efficient do it like S(x) does since no advanced opts */
2365 if (!play_warning && !end_sound && timelimit) {
2366 calldurationlimit = timelimit / 1000;
2367 timelimit = play_warning = warning_freq = 0;
2369 ast_debug(2, "Limit Data for this call:\n");
2370 ast_debug(2, "- timelimit = %ld\n", timelimit);
2371 ast_debug(2, "- play_warning = %ld\n", play_warning);
2372 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
2373 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
2374 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
2379 if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
2380 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
2381 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
2383 exitkeys = ast_strdupa("#"); /* Default */
2386 if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
2387 if (!conf->recordingfilename) {
2389 ast_channel_lock(chan);
2390 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
2391 conf->recordingfilename = ast_strdup(var);
2393 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
2394 conf->recordingformat = ast_strdup(var);
2396 ast_channel_unlock(chan);
2397 if (!conf->recordingfilename) {
2398 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
2399 conf->recordingfilename = ast_strdup(recordingtmp);
2401 if (!conf->recordingformat) {
2402 conf->recordingformat = ast_strdup("wav");
2404 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
2405 conf->confno, conf->recordingfilename, conf->recordingformat);
2409 ast_mutex_lock(&conf->recordthreadlock);
2410 if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
2411 ((conf->lchan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL)))) {
2412 ast_set_read_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
2413 ast_set_write_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
2415 dahdic.confno = conf->dahdiconf;
2416 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
2417 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
2418 ast_log(LOG_WARNING, "Error starting listen channel\n");
2419 ast_hangup(conf->lchan);
2422 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
2425 ast_mutex_unlock(&conf->recordthreadlock);
2427 ast_mutex_lock(&conf->announcethreadlock);
2428 if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
2429 ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC)) {
2430 ast_mutex_init(&conf->announcelistlock);
2431 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2432 ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
2434 ast_mutex_unlock(&conf->announcethreadlock);
2436 time(&user->jointime);
2438 user->timelimit = timelimit;
2439 user->play_warning = play_warning;
2440 user->warning_freq = warning_freq;
2441 user->warning_sound = warning_sound;
2442 user->end_sound = end_sound;
2444 if (calldurationlimit > 0) {
2445 time(&user->kicktime);
2446 user->kicktime = user->kicktime + calldurationlimit;
2449 if (ast_tvzero(user->start_time))
2450 user->start_time = ast_tvnow();
2451 time_left_ms = user->timelimit;
2453 if (user->timelimit) {
2454 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2455 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
2458 if (conf->locked && (!ast_test_flag64(confflags, CONFFLAG_ADMIN))) {
2459 /* Sorry, but this conference is locked! */
2460 if (!ast_streamfile(chan, "conf-locked", chan->language))
2461 ast_waitstream(chan, "");
2465 ast_mutex_lock(&conf->playlock);
2467 if (rt_schedule && conf->maxusers) {
2468 if (conf->users >= conf->maxusers) {
2469 /* Sorry, but this confernce has reached the participant limit! */
2470 if (!ast_streamfile(chan, "conf-full", chan->language))
2471 ast_waitstream(chan, "");
2472 ast_mutex_unlock(&conf->playlock);
2477 ao2_lock(conf->usercontainer);
2478 ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &user->user_no);
2480 ao2_link(conf->usercontainer, user);
2481 ao2_unlock(conf->usercontainer);
2484 user->userflags = *confflags;
2485 user->adminflags = ast_test_flag64(confflags, CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
2486 user->adminflags |= (conf->gmuted) ? ADMINFLAG_MUTED : 0;
2489 ast_mutex_unlock(&conf->playlock);
2491 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC))) {
2492 char destdir[PATH_MAX];
2494 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
2496 if (ast_mkdir(destdir, 0777) != 0) {
2497 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
2501 if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
2502 context = ast_strdupa(optargs[OPT_ARG_INTROUSER_VMREC]);
2503 mailbox = strsep(&context, "@");
2505 if (ast_strlen_zero(mailbox)) {
2506 /* invalid input, clear the v flag*/
2507 ast_clear_flag64(confflags,CONFFLAG_INTROUSER_VMREC);
2508 ast_log(LOG_WARNING,"You must specify a mailbox in the v() option\n");
2510 if (ast_strlen_zero(context)) {
2511 context = "default";
2513 /* if there is no mailbox we don't need to do this logic */
2514 snprintf(user->namerecloc, sizeof(user->namerecloc),
2515 "%s/voicemail/%s/%s/greet",ast_config_AST_SPOOL_DIR,context,mailbox);
2517 /* if the greeting doesn't exist then use the temp file method instead, clear flag v */
2518 if (!ast_fileexists(user->namerecloc, NULL, NULL)){
2519 snprintf(user->namerecloc, sizeof(user->namerecloc),
2520 "%s/meetme-username-%s-%d", destdir,
2521 conf->confno, user->user_no);
2522 ast_clear_flag64(confflags, CONFFLAG_INTROUSER_VMREC);
2526 snprintf(user->namerecloc, sizeof(user->namerecloc),
2527 "%s/meetme-username-%s-%d", destdir,
2528 conf->confno, user->user_no);
2532 if (ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) && !ast_fileexists(user->namerecloc, NULL, NULL))
2533 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
2534 else if (ast_test_flag64(confflags, CONFFLAG_INTROUSER) && !ast_fileexists(user->namerecloc, NULL, NULL))
2535 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
2541 ast_mutex_lock(&conf->playlock);
2543 if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER))
2544 conf->markedusers++;
2546 if (rt_log_members) {
2548 snprintf(members, sizeof(members), "%d", conf->users);
2549 ast_realtime_require_field("meetme",
2550 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
2551 "members", RQ_UINTEGER1, strlen(members),
2553 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2557 /* This device changed state now - if this is the first user */
2558 if (conf->users == 1)
2559 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
2561 ast_mutex_unlock(&conf->playlock);
2563 /* return the unique ID of the conference */
2564 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
2566 if (ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT)) {
2567 ast_channel_lock(chan);
2568 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
2569 ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
2570 } else if (!ast_strlen_zero(chan->macrocontext)) {
2571 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
2573 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
2575 ast_channel_unlock(chan);
2578 /* Play an arbitrary intro message */
2579 if (ast_test_flag64(confflags, CONFFLAG_INTROMSG) &&
2580 !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
2581 if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], chan->language)) {
2582 ast_waitstream(chan, "");
2586 if (!ast_test_flag64(confflags, (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
2587 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED))
2588 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
2589 ast_waitstream(chan, "");
2590 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && conf->markedusers == 0)
2591 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
2592 ast_waitstream(chan, "");
2595 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_ANNOUNCEUSERCOUNT) &&
2597 int keepplaying = 1;
2599 if (conf->users == 2) {
2600 if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
2601 res = ast_waitstream(chan, AST_DIGIT_ANY);
2602 ast_stopstream(chan);
2609 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
2610 res = ast_waitstream(chan, AST_DIGIT_ANY);
2611 ast_stopstream(chan);
2618 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2624 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
2625 res = ast_waitstream(chan, AST_DIGIT_ANY);
2626 ast_stopstream(chan);
2635 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
2636 /* We're leaving this alone until the state gets changed to up */
2637 ast_indicate(chan, -1);
2640 if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
2641 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
2645 if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
2646 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
2650 /* Reduce background noise from each participant */
2651 if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
2652 ast_free(mod_speex);
2653 ast_func_write(chan, "DENOISE(rx)", "on");
2656 retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
2657 user->dahdichannel = !retrydahdi;
2660 origfd = chan->fds[0];
2662 /* open pseudo in non-blocking mode */
2663 fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
2665 ast_log(LOG_WARNING, "Unable to open DAHDI pseudo channel: %s\n", strerror(errno));
2669 /* Setup buffering information */
2670 memset(&bi, 0, sizeof(bi));
2671 bi.bufsize = CONF_SIZE / 2;
2672 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
2673 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
2674 bi.numbufs = audio_buffers;
2675 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
2676 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
2681 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
2682 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
2688 /* XXX Make sure we're not running on a pseudo channel XXX */
2692 memset(&dahdic, 0, sizeof(dahdic));
2693 memset(&dahdic_empty, 0, sizeof(dahdic_empty));
2694 /* Check to see if we're in a conference... */
2696 if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
2697 ast_log(LOG_WARNING, "Error getting conference\n");
2701 if (dahdic.confmode) {
2702 /* Whoa, already in a conference... Retry... */
2704 ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
2709 memset(&dahdic, 0, sizeof(dahdic));
2710 /* Add us to the conference */
2712 dahdic.confno = conf->dahdiconf;
2714 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
2715 ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) || ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)) && conf->users > 1) {
2716 struct announce_listitem *item;
2717 if (!(item = ao2_alloc(sizeof(*item), NULL)))
2719 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
2720 ast_copy_string(item->language, chan->language, sizeof(item->language));
2721 item->confchan = conf->chan;
2722 item->confusers = conf->users;
2723 if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
2726 item->announcetype = CONF_HASJOIN;
2727 ast_mutex_lock(&conf->announcelistlock);
2728 ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
2729 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
2730 ast_cond_signal(&conf->announcelist_addition);
2731 ast_mutex_unlock(&conf->announcelistlock);
2733 while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
2739 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && !conf->markedusers)
2740 dahdic.confmode = DAHDI_CONF_CONF;
2741 else if (ast_test_flag64(confflags, CONFFLAG_MONITOR))
2742 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2743 else if (ast_test_flag64(confflags, CONFFLAG_TALKER))
2744 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2746 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2748 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2749 ast_log(LOG_WARNING, "Error setting conference\n");
2753 ast_debug(1, "Placed channel %s in DAHDI conf %d\n", chan->name, conf->dahdiconf);
2756 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeJoin",
2761 "CallerIDnum: %s\r\n"
2762 "CallerIDname: %s\r\n"
2763 "ConnectedLineNum: %s\r\n"
2764 "ConnectedLineName: %s\r\n",
2765 chan->name, chan->uniqueid, conf->confno,
2767 S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
2768 S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<unknown>"),
2769 S_COR(user->chan->connected.id.number.valid, user->chan->connected.id.number.str, "<unknown>"),
2770 S_COR(user->chan->connected.id.name.valid, user->chan->connected.id.name.str, "<unknown>")
2775 if (!firstpass && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
2776 !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
2778 if (!ast_test_flag64(confflags, CONFFLAG_QUIET))
2779 if (!ast_test_flag64(confflags, CONFFLAG_WAITMARKED) || (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
2780 (conf->markedusers >= 1))) {
2781 conf_play(chan, conf, ENTER);
2785 conf_flush(fd, chan);
2790 if (!(dsp = ast_dsp_new())) {
2791 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
2795 if (ast_test_flag64(confflags, CONFFLAG_AGI)) {
2796 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
2797 or use default filename of conf-background.agi */
2799 ast_channel_lock(chan);
2800 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
2801 agifile = ast_strdupa(tmpvar);
2803 agifile = ast_strdupa(agifiledefault);
2805 ast_channel_unlock(chan);
2807 if (user->dahdichannel) {
2808 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
2810 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2812 /* Find a pointer to the agi app and execute the script */
2813 agi_app = pbx_findapp("agi");
2815 ret = pbx_exec(chan, agi_app, agifile);
2817 ast_log(LOG_WARNING, "Could not find application (agi)\n");
2820 if (user->dahdichannel) {
2821 /* Remove CONFMUTE mode on DAHDI channel */
2823 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2826 if (user->dahdichannel && ast_test_flag64(confflags, CONFFLAG_STARMENU)) {
2827 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
2829 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2832 int menu_was_active = 0;
2838 if (rt_schedule && conf->endtime) {
2839 char currenttime[32];
2840 long localendtime = 0;
2843 struct ast_variable *var, *origvar;
2846 if (now.tv_sec % 60 == 0) {
2848 ast_localtime(&now, &tm, NULL);
2849 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2850 var = origvar = ast_load_realtime("meetme", "confno",
2851 conf->confno, "starttime <=", currenttime,
2852 "endtime >=", currenttime, NULL);
2854 for ( ; var; var = var->next) {
2855 if (!strcasecmp(var->name, "endtime")) {
2856 struct ast_tm endtime_tm;
2857 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
2858 tmp = ast_mktime(&endtime_tm, NULL);
2859 localendtime = tmp.tv_sec;
2862 ast_variables_destroy(origvar);
2864 /* A conference can be extended from the
2865 Admin/User menu or by an external source */
2866 if (localendtime > conf->endtime){
2867 conf->endtime = localendtime;
2871 if (conf->endtime && (now.tv_sec >= conf->endtime)) {
2872 ast_verbose("Quitting time...\n");
2876 if (!announcement_played && conf->endalert) {
2877 if (now.tv_sec + conf->endalert >= conf->endtime) {
2878 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
2879 ast_waitstream(chan, "");
2880 ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", chan->language);
2881 if (!ast_streamfile(chan, "minutes", chan->language))
2882 ast_waitstream(chan, "");
2884 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2886 announcement_played = 1;