2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2007, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * SLA Implementation by:
9 * Russell Bryant <russell@digium.com>
11 * See http://www.asterisk.org for more information about
12 * the Asterisk project. Please do not directly contact
13 * any of the maintainers of this project for assistance;
14 * the project provides a web site, mailing lists and IRC
15 * channels for your use.
17 * This program is free software, distributed under the terms of
18 * the GNU General Public License Version 2. See the LICENSE file
19 * at the top of the source tree.
24 * \brief Meet me conference bridge and Shared Line Appearances
26 * \author Mark Spencer <markster@digium.com>
27 * \author (SLA) Russell Bryant <russell@digium.com>
29 * \ingroup applications
33 <depend>dahdi</depend>
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
40 #include <dahdi/user.h>
42 #include "asterisk/lock.h"
43 #include "asterisk/file.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/pbx.h"
46 #include "asterisk/module.h"
47 #include "asterisk/config.h"
48 #include "asterisk/app.h"
49 #include "asterisk/dsp.h"
50 #include "asterisk/musiconhold.h"
51 #include "asterisk/manager.h"
52 #include "asterisk/cli.h"
53 #include "asterisk/say.h"
54 #include "asterisk/utils.h"
55 #include "asterisk/translate.h"
56 #include "asterisk/ulaw.h"
57 #include "asterisk/astobj2.h"
58 #include "asterisk/devicestate.h"
59 #include "asterisk/dial.h"
60 #include "asterisk/causes.h"
61 #include "asterisk/paths.h"
62 #include "asterisk/data.h"
63 #include "asterisk/test.h"
69 <application name="MeetMe" language="en_US">
71 MeetMe conference bridge.
74 <parameter name="confno">
75 <para>The conference number</para>
77 <parameter name="options">
80 <para>Set admin mode.</para>
83 <para>Set marked mode.</para>
86 <para>Run AGI script specified in <variable>MEETME_AGI_BACKGROUND</variable>
87 Default: <literal>conf-background.agi</literal>.</para>
88 <note><para>This does not work with non-DAHDI channels in the same
89 conference).</para></note>
92 <para>Announce user(s) count on joining a conference.</para>
95 <para>Continue in dialplan when kicked out of conference.</para>
98 <para>Dynamically add conference.</para>
101 <para>Dynamically add conference, prompting for a PIN.</para>
104 <para>Select an empty conference.</para>
107 <para>Select an empty pinless conference.</para>
110 <para>Pass DTMF through the conference.</para>
113 <argument name="x" required="true">
114 <para>The file to playback</para>
116 <para>Play an intro announcement in conference.</para>
119 <para>Announce user join/leave with review.</para>
122 <para>Announce user join/leave without review.</para>
125 <para>Set listen only mode (Listen only, no talking).</para>
128 <para>Set initially muted.</para>
130 <option name="M" hasparams="optional">
131 <para>Enable music on hold when the conference has a single caller. Optionally,
132 specify a musiconhold class to use. If one is not provided, it will use the
133 channel's currently set music class, or <literal>default</literal>.</para>
134 <argument name="class" required="true" />
137 <para>Set talker optimization - treats talkers who aren't speaking as
138 being muted, meaning (a) No encode is done on transmission and (b)
139 Received audio that is not registered as talking is omitted causing no
140 buildup in background noise.</para>
142 <option name="p" hasparams="optional">
143 <para>Allow user to exit the conference by pressing <literal>#</literal> (default)
144 or any of the defined keys. If keys contain <literal>*</literal> this will override
145 option <literal>s</literal>. The key used is set to channel variable
146 <variable>MEETME_EXIT_KEY</variable>.</para>
147 <argument name="keys" required="true" />
150 <para>Always prompt for the pin even if it is specified.</para>
153 <para>Quiet mode (don't play enter/leave sounds).</para>
156 <para>Record conference (records as <variable>MEETME_RECORDINGFILE</variable>
157 using format <variable>MEETME_RECORDINGFORMAT</variable>. Default filename is
158 <literal>meetme-conf-rec-${CONFNO}-${UNIQUEID}</literal> and the default format is
162 <para>Present menu (user or admin) when <literal>*</literal> is received
163 (send to menu).</para>
166 <para>Set talk only mode. (Talk only, no listening).</para>
169 <para>Set talker detection (sent to manager interface and meetme list).</para>
171 <option name="w" hasparams="optional">
172 <para>Wait until the marked user enters the conference.</para>
173 <argument name="secs" required="true" />
176 <para>Close the conference when last marked user exits</para>
179 <para>Allow user to exit the conference by entering a valid single digit
180 extension <variable>MEETME_EXIT_CONTEXT</variable> or the current context
181 if that variable is not defined.</para>
184 <para>Do not play message when first person enters</para>
187 <para>Kick the user <replaceable>x</replaceable> seconds <emphasis>after</emphasis> he entered into
188 the conference.</para>
189 <argument name="x" required="true" />
191 <option name="L" argsep=":">
192 <para>Limit the conference to <replaceable>x</replaceable> ms. Play a warning when
193 <replaceable>y</replaceable> ms are left. Repeat the warning every <replaceable>z</replaceable> ms.
194 The following special variables can be used with this option:</para>
196 <variable name="CONF_LIMIT_TIMEOUT_FILE">
197 <para>File to play when time is up.</para>
199 <variable name="CONF_LIMIT_WARNING_FILE">
200 <para>File to play as warning if <replaceable>y</replaceable> is defined. The
201 default is to say the time remaining.</para>
204 <argument name="x" />
205 <argument name="y" />
206 <argument name="z" />
210 <parameter name="pin" />
213 <para>Enters the user into a specified MeetMe conference. If the <replaceable>confno</replaceable>
214 is omitted, the user will be prompted to enter one. User can exit the conference by hangup, or
215 if the <literal>p</literal> option is specified, by pressing <literal>#</literal>.</para>
216 <note><para>The DAHDI kernel modules and at least one hardware driver (or dahdi_dummy)
217 must be present for conferencing to operate properly. In addition, the chan_dahdi channel driver
218 must be loaded for the <literal>i</literal> and <literal>r</literal> options to operate at
222 <ref type="application">MeetMeCount</ref>
223 <ref type="application">MeetMeAdmin</ref>
224 <ref type="application">MeetMeChannelAdmin</ref>
227 <application name="MeetMeCount" language="en_US">
229 MeetMe participant count.
232 <parameter name="confno" required="true">
233 <para>Conference number.</para>
235 <parameter name="var" />
238 <para>Plays back the number of users in the specified MeetMe conference.
239 If <replaceable>var</replaceable> is specified, playback will be skipped and the value
240 will be returned in the variable. Upon application completion, MeetMeCount will hangup
241 the channel, unless priority <literal>n+1</literal> exists, in which case priority progress will
245 <ref type="application">MeetMe</ref>
248 <application name="MeetMeAdmin" language="en_US">
250 MeetMe conference administration.
253 <parameter name="confno" required="true" />
254 <parameter name="command" required="true">
257 <para>Eject last user that joined.</para>
260 <para>Extend conference end time, if scheduled.</para>
263 <para>Kick one user out of conference.</para>
266 <para>Kick all users out of conference.</para>
269 <para>Unlock conference.</para>
272 <para>Lock conference.</para>
275 <para>Unmute one user.</para>
278 <para>Mute one user.</para>
281 <para>Unmute all users in the conference.</para>
284 <para>Mute all non-admin users in the conference.</para>
287 <para>Reset one user's volume settings.</para>
290 <para>Reset all users volume settings.</para>
293 <para>Lower entire conference speaking volume.</para>
296 <para>Raise entire conference speaking volume.</para>
299 <para>Lower one user's talk volume.</para>
302 <para>Raise one user's talk volume.</para>
305 <para>Lower one user's listen volume.</para>
308 <para>Raise one user's listen volume.</para>
311 <para>Lower entire conference listening volume.</para>
314 <para>Raise entire conference listening volume.</para>
318 <parameter name="user" />
321 <para>Run admin <replaceable>command</replaceable> for conference <replaceable>confno</replaceable>.</para>
322 <para>Will additionally set the variable <variable>MEETMEADMINSTATUS</variable> with one of
323 the following values:</para>
325 <variable name="MEETMEADMINSTATUS">
326 <value name="NOPARSE">
329 <value name="NOTFOUND">
330 User specified was not found.
332 <value name="FAILED">
333 Another failure occurred.
336 The operation was completed successfully.
342 <ref type="application">MeetMe</ref>
345 <application name="MeetMeChannelAdmin" language="en_US">
347 MeetMe conference Administration (channel specific).
350 <parameter name="channel" required="true" />
351 <parameter name="command" required="true">
354 <para>Kick the specified user out of the conference he is in.</para>
357 <para>Unmute the specified user.</para>
360 <para>Mute the specified user.</para>
366 <para>Run admin <replaceable>command</replaceable> for a specific
367 <replaceable>channel</replaceable> in any coference.</para>
370 <application name="SLAStation" language="en_US">
372 Shared Line Appearance Station.
375 <parameter name="station" required="true">
376 <para>Station name</para>
380 <para>This application should be executed by an SLA station. The argument depends
381 on how the call was initiated. If the phone was just taken off hook, then the argument
382 <replaceable>station</replaceable> should be just the station name. If the call was
383 initiated by pressing a line key, then the station name should be preceded by an underscore
384 and the trunk name associated with that line button.</para>
385 <para>For example: <literal>station1_line1</literal></para>
386 <para>On exit, this application will set the variable <variable>SLASTATION_STATUS</variable> to
387 one of the following values:</para>
389 <variable name="SLASTATION_STATUS">
390 <value name="FAILURE" />
391 <value name="CONGESTION" />
392 <value name="SUCCESS" />
397 <application name="SLATrunk" language="en_US">
399 Shared Line Appearance Trunk.
402 <parameter name="trunk" required="true">
403 <para>Trunk name</para>
405 <parameter name="options">
407 <option name="M" hasparams="optional">
408 <para>Play back the specified MOH <replaceable>class</replaceable>
409 instead of ringing</para>
410 <argument name="class" required="true" />
416 <para>This application should be executed by an SLA trunk on an inbound call. The channel calling
417 this application should correspond to the SLA trunk with the name <replaceable>trunk</replaceable>
418 that is being passed as an argument.</para>
419 <para>On exit, this application will set the variable <variable>SLATRUNK_STATUS</variable> to
420 one of the following values:</para>
422 <variable name="SLATRUNK_STATUS">
423 <value name="FAILURE" />
424 <value name="SUCCESS" />
425 <value name="UNANSWERED" />
426 <value name="RINGTIMEOUT" />
431 <function name="MEETME_INFO" language="en_US">
433 Query a given conference of various properties.
436 <parameter name="keyword" required="true">
437 <para>Options:</para>
440 <para>Boolean of whether the corresponding conference is locked.</para>
442 <enum name="parties">
443 <para>Number of parties in a given conference</para>
445 <enum name="activity">
446 <para>Duration of conference in seconds.</para>
448 <enum name="dynamic">
449 <para>Boolean of whether the corresponding conference is dynamic.</para>
453 <parameter name="confno" required="true">
454 <para>Conference number to retrieve information from.</para>
459 <ref type="application">MeetMe</ref>
460 <ref type="application">MeetMeCount</ref>
461 <ref type="application">MeetMeAdmin</ref>
462 <ref type="application">MeetMeChannelAdmin</ref>
465 <manager name="MeetmeMute" language="en_US">
470 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
471 <parameter name="Meetme" required="true" />
472 <parameter name="Usernum" required="true" />
477 <manager name="MeetmeUnmute" language="en_US">
479 Unmute a Meetme user.
482 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
483 <parameter name="Meetme" required="true" />
484 <parameter name="Usernum" required="true" />
489 <manager name="MeetmeList" language="en_US">
491 List participants in a conference.
494 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
495 <parameter name="Conference" required="true">
496 <para>Conference number.</para>
500 <para>Lists all users in a particular MeetMe conference.
501 MeetmeList will follow as separate events, followed by a final event called
502 MeetmeListComplete.</para>
507 #define CONFIG_FILE_NAME "meetme.conf"
508 #define SLA_CONFIG_FILE "sla.conf"
510 /*! each buffer is 20ms, so this is 640ms total */
511 #define DEFAULT_AUDIO_BUFFERS 32
513 /*! String format for scheduled conferences */
514 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
517 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
518 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
519 ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */
520 /*! User has requested to speak */
521 ADMINFLAG_T_REQUEST = (1 << 4),
524 #define MEETME_DELAYDETECTTALK 300
525 #define MEETME_DELAYDETECTENDTALK 1000
527 #define AST_FRAME_BITS 32
534 enum entrance_sound {
539 enum recording_state {
541 MEETME_RECORD_STARTED,
542 MEETME_RECORD_ACTIVE,
543 MEETME_RECORD_TERMINATE
546 #define CONF_SIZE 320
549 /*! user has admin access on the conference */
550 CONFFLAG_ADMIN = (1 << 0),
551 /*! If set the user can only receive audio from the conference */
552 CONFFLAG_MONITOR = (1 << 1),
553 /*! If set asterisk will exit conference when key defined in p() option is pressed */
554 CONFFLAG_KEYEXIT = (1 << 2),
555 /*! If set asterisk will provide a menu to the user when '*' is pressed */
556 CONFFLAG_STARMENU = (1 << 3),
557 /*! If set the use can only send audio to the conference */
558 CONFFLAG_TALKER = (1 << 4),
559 /*! If set there will be no enter or leave sounds */
560 CONFFLAG_QUIET = (1 << 5),
561 /*! If set, when user joins the conference, they will be told the number
562 * of users that are already in */
563 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
564 /*! Set to run AGI Script in Background */
565 CONFFLAG_AGI = (1 << 7),
566 /*! Set to have music on hold when user is alone in conference */
567 CONFFLAG_MOH = (1 << 8),
568 /*! If set the MeetMe will return if all marked with this flag left */
569 CONFFLAG_MARKEDEXIT = (1 << 9),
570 /*! If set, the MeetMe will wait until a marked user enters */
571 CONFFLAG_WAITMARKED = (1 << 10),
572 /*! If set, the MeetMe will exit to the specified context */
573 CONFFLAG_EXIT_CONTEXT = (1 << 11),
574 /*! If set, the user will be marked */
575 CONFFLAG_MARKEDUSER = (1 << 12),
576 /*! If set, user will be ask record name on entry of conference */
577 CONFFLAG_INTROUSER = (1 << 13),
578 /*! If set, the MeetMe will be recorded */
579 CONFFLAG_RECORDCONF = (1<< 14),
580 /*! If set, the user will be monitored if the user is talking or not */
581 CONFFLAG_MONITORTALKER = (1 << 15),
582 CONFFLAG_DYNAMIC = (1 << 16),
583 CONFFLAG_DYNAMICPIN = (1 << 17),
584 CONFFLAG_EMPTY = (1 << 18),
585 CONFFLAG_EMPTYNOPIN = (1 << 19),
586 CONFFLAG_ALWAYSPROMPT = (1 << 20),
587 /*! If set, treat talking users as muted users */
588 CONFFLAG_OPTIMIZETALKER = (1 << 21),
589 /*! If set, won't speak the extra prompt when the first person
590 * enters the conference */
591 CONFFLAG_NOONLYPERSON = (1 << 22),
592 /*! If set, user will be asked to record name on entry of conference
594 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
595 /*! If set, the user will be initially self-muted */
596 CONFFLAG_STARTMUTED = (1 << 24),
597 /*! Pass DTMF through the conference */
598 CONFFLAG_PASS_DTMF = (1 << 25),
599 CONFFLAG_SLA_STATION = (1 << 26),
600 CONFFLAG_SLA_TRUNK = (1 << 27),
601 /*! If set, the user should continue in the dialplan if kicked out */
602 CONFFLAG_KICK_CONTINUE = (1 << 28),
603 CONFFLAG_DURATION_STOP = (1 << 29),
604 CONFFLAG_DURATION_LIMIT = (1 << 30),
605 /*! Do not write any audio to this channel until the state is up. */
606 CONFFLAG_NO_AUDIO_UNTIL_UP = (1 << 31),
609 /* !If set play an intro announcement at start of conference */
610 #define CONFFLAG_INTROMSG ((uint64_t)1 << 32)
613 OPT_ARG_WAITMARKED = 0,
614 OPT_ARG_EXITKEYS = 1,
615 OPT_ARG_DURATION_STOP = 2,
616 OPT_ARG_DURATION_LIMIT = 3,
617 OPT_ARG_MOH_CLASS = 4,
618 OPT_ARG_INTROMSG = 5,
619 OPT_ARG_ARRAY_SIZE = 6,
622 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
623 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
624 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
625 AST_APP_OPTION('b', CONFFLAG_AGI ),
626 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
627 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
628 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
629 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
630 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
631 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
632 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
633 AST_APP_OPTION_ARG('G', CONFFLAG_INTROMSG, OPT_ARG_INTROMSG ),
634 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
635 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
636 AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
637 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
638 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
639 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
640 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
641 AST_APP_OPTION('q', CONFFLAG_QUIET ),
642 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
643 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
644 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
645 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
646 AST_APP_OPTION('t', CONFFLAG_TALKER ),
647 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
648 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
649 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
650 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
651 AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
652 AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
655 static const char * const app = "MeetMe";
656 static const char * const app2 = "MeetMeCount";
657 static const char * const app3 = "MeetMeAdmin";
658 static const char * const app4 = "MeetMeChannelAdmin";
659 static const char * const slastation_app = "SLAStation";
660 static const char * const slatrunk_app = "SLATrunk";
662 /* Lookup RealTime conferences based on confno and current time */
663 static int rt_schedule;
664 static int fuzzystart;
665 static int earlyalert;
669 /*! Log participant count to the RealTime backend */
670 static int rt_log_members;
672 #define MAX_CONFNUM 80
674 #define OPTIONS_LEN 100
676 /* Enough space for "<conference #>,<pin>,<admin pin>" followed by a 0 byte. */
677 #define MAX_SETTINGS (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)
684 struct announce_listitem {
685 AST_LIST_ENTRY(announce_listitem) entry;
686 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
687 char language[MAX_LANGUAGE];
688 struct ast_channel *confchan;
690 enum announcetypes announcetype;
693 /*! \brief The MeetMe Conference object */
694 struct ast_conference {
695 ast_mutex_t playlock; /*!< Conference specific lock (players) */
696 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
697 char confno[MAX_CONFNUM]; /*!< Conference */
698 struct ast_channel *chan; /*!< Announcements channel */
699 struct ast_channel *lchan; /*!< Listen/Record channel */
700 int fd; /*!< Announcements fd */
701 int dahdiconf; /*!< DAHDI Conf # */
702 int users; /*!< Number of active users */
703 int markedusers; /*!< Number of marked users */
704 int maxusers; /*!< Participant limit if scheduled */
705 int endalert; /*!< When to play conf ending message */
706 time_t start; /*!< Start time (s) */
707 int refcount; /*!< reference count of usage */
708 enum recording_state recording:2; /*!< recording status */
709 unsigned int isdynamic:1; /*!< Created on the fly? */
710 unsigned int locked:1; /*!< Is the conference locked? */
711 unsigned int gmuted:1; /*!< Is the conference globally muted? (all non-admins) */
712 pthread_t recordthread; /*!< thread for recording */
713 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
714 pthread_attr_t attr; /*!< thread attribute */
715 char *recordingfilename; /*!< Filename to record the Conference into */
716 char *recordingformat; /*!< Format to record the Conference in */
717 char pin[MAX_PIN]; /*!< If protected by a PIN */
718 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
720 long endtime; /*!< When to end the conf if scheduled */
721 const char *useropts; /*!< RealTime user flags */
722 const char *adminopts; /*!< RealTime moderator flags */
723 const char *bookid; /*!< RealTime conference id */
724 struct ast_frame *transframe[32];
725 struct ast_frame *origframe;
726 struct ast_trans_pvt *transpath[32];
727 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
728 AST_LIST_ENTRY(ast_conference) list;
729 /* announce_thread related data */
730 pthread_t announcethread;
731 ast_mutex_t announcethreadlock;
732 unsigned int announcethread_stop:1;
733 ast_cond_t announcelist_addition;
734 AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
735 ast_mutex_t announcelistlock;
738 static AST_LIST_HEAD_STATIC(confs, ast_conference);
740 static unsigned int conf_map[1024] = {0, };
743 int desired; /*!< Desired volume adjustment */
744 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
747 /*! \brief The MeetMe User object */
748 struct ast_conf_user {
749 int user_no; /*!< User Number */
750 struct ast_flags64 userflags; /*!< Flags as set in the conference */
751 int adminflags; /*!< Flags set by the Admin */
752 struct ast_channel *chan; /*!< Connected channel */
753 int talking; /*!< Is user talking */
754 int dahdichannel; /*!< Is a DAHDI channel */
755 char usrvalue[50]; /*!< Custom User Value */
756 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
757 time_t jointime; /*!< Time the user joined the conference */
758 time_t kicktime; /*!< Time the user will be kicked from the conference */
759 struct timeval start_time; /*!< Time the user entered into the conference */
760 long timelimit; /*!< Time limit for the user to be in the conference L(x:y:z) */
761 long play_warning; /*!< Play a warning when 'y' ms are left */
762 long warning_freq; /*!< Repeat the warning every 'z' ms */
763 const char *warning_sound; /*!< File to play as warning if 'y' is defined */
764 const char *end_sound; /*!< File to play when time is up. */
766 struct volume listen;
767 AST_LIST_ENTRY(ast_conf_user) list;
770 enum sla_which_trunk_refs {
775 enum sla_trunk_state {
776 SLA_TRUNK_STATE_IDLE,
777 SLA_TRUNK_STATE_RINGING,
779 SLA_TRUNK_STATE_ONHOLD,
780 SLA_TRUNK_STATE_ONHOLD_BYME,
783 enum sla_hold_access {
784 /*! This means that any station can put it on hold, and any station
785 * can retrieve the call from hold. */
787 /*! This means that only the station that put the call on hold may
788 * retrieve it from hold. */
792 struct sla_trunk_ref;
795 AST_RWLIST_ENTRY(sla_station) entry;
796 AST_DECLARE_STRING_FIELDS(
797 AST_STRING_FIELD(name);
798 AST_STRING_FIELD(device);
799 AST_STRING_FIELD(autocontext);
801 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
802 struct ast_dial *dial;
803 /*! Ring timeout for this station, for any trunk. If a ring timeout
804 * is set for a specific trunk on this station, that will take
805 * priority over this value. */
806 unsigned int ring_timeout;
807 /*! Ring delay for this station, for any trunk. If a ring delay
808 * is set for a specific trunk on this station, that will take
809 * priority over this value. */
810 unsigned int ring_delay;
811 /*! This option uses the values in the sla_hold_access enum and sets the
812 * access control type for hold on this station. */
813 unsigned int hold_access:1;
814 /*! Use count for inside sla_station_exec */
815 unsigned int ref_count;
818 struct sla_station_ref {
819 AST_LIST_ENTRY(sla_station_ref) entry;
820 struct sla_station *station;
824 AST_RWLIST_ENTRY(sla_trunk) entry;
825 AST_DECLARE_STRING_FIELDS(
826 AST_STRING_FIELD(name);
827 AST_STRING_FIELD(device);
828 AST_STRING_FIELD(autocontext);
830 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
831 /*! Number of stations that use this trunk */
832 unsigned int num_stations;
833 /*! Number of stations currently on a call with this trunk */
834 unsigned int active_stations;
835 /*! Number of stations that have this trunk on hold. */
836 unsigned int hold_stations;
837 struct ast_channel *chan;
838 unsigned int ring_timeout;
839 /*! If set to 1, no station will be able to join an active call with
841 unsigned int barge_disabled:1;
842 /*! This option uses the values in the sla_hold_access enum and sets the
843 * access control type for hold on this trunk. */
844 unsigned int hold_access:1;
845 /*! Whether this trunk is currently on hold, meaning that once a station
846 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
847 unsigned int on_hold:1;
848 /*! Use count for inside sla_trunk_exec */
849 unsigned int ref_count;
852 struct sla_trunk_ref {
853 AST_LIST_ENTRY(sla_trunk_ref) entry;
854 struct sla_trunk *trunk;
855 enum sla_trunk_state state;
856 struct ast_channel *chan;
857 /*! Ring timeout to use when this trunk is ringing on this specific
858 * station. This takes higher priority than a ring timeout set at
859 * the station level. */
860 unsigned int ring_timeout;
861 /*! Ring delay to use when this trunk is ringing on this specific
862 * station. This takes higher priority than a ring delay set at
863 * the station level. */
864 unsigned int ring_delay;
867 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
868 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
870 static const char sla_registrar[] = "SLA";
872 /*! \brief Event types that can be queued up for the SLA thread */
873 enum sla_event_type {
874 /*! A station has put the call on hold */
876 /*! The state of a dial has changed */
877 SLA_EVENT_DIAL_STATE,
878 /*! The state of a ringing trunk has changed */
879 SLA_EVENT_RINGING_TRUNK,
880 /*! A reload of configuration has been requested */
882 /*! Poke the SLA thread so it can check if it can perform a reload */
883 SLA_EVENT_CHECK_RELOAD,
887 enum sla_event_type type;
888 struct sla_station *station;
889 struct sla_trunk_ref *trunk_ref;
890 AST_LIST_ENTRY(sla_event) entry;
893 /*! \brief A station that failed to be dialed
894 * \note Only used by the SLA thread. */
895 struct sla_failed_station {
896 struct sla_station *station;
897 struct timeval last_try;
898 AST_LIST_ENTRY(sla_failed_station) entry;
901 /*! \brief A trunk that is ringing */
902 struct sla_ringing_trunk {
903 struct sla_trunk *trunk;
904 /*! The time that this trunk started ringing */
905 struct timeval ring_begin;
906 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
907 AST_LIST_ENTRY(sla_ringing_trunk) entry;
910 enum sla_station_hangup {
911 SLA_STATION_HANGUP_NORMAL,
912 SLA_STATION_HANGUP_TIMEOUT,
915 /*! \brief A station that is ringing */
916 struct sla_ringing_station {
917 struct sla_station *station;
918 /*! The time that this station started ringing */
919 struct timeval ring_begin;
920 AST_LIST_ENTRY(sla_ringing_station) entry;
924 * \brief A structure for data used by the sla thread
927 /*! The SLA thread ID */
931 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
932 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
933 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
934 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
936 /*! Attempt to handle CallerID, even though it is known not to work
937 * properly in some situations. */
938 unsigned int attempt_callerid:1;
939 /*! A reload has been requested */
940 unsigned int reload:1;
942 .thread = AST_PTHREADT_NULL,
945 /*! \brief The number of audio buffers to be allocated on pseudo channels
946 * when in a conference */
947 static int audio_buffers;
949 /*! \brief Map 'volume' levels from -5 through +5 into decibel (dB)
950 * settings for channel drivers.
952 * \note these are not a straight linear-to-dB
953 * conversion... the numbers have been modified
954 * to give the user a better level of adjustability.
956 static const char gain_map[] = {
971 static int admin_exec(struct ast_channel *chan, const char *data);
972 static void *recordthread(void *args);
974 static const char *istalking(int x)
979 return "(unmonitored)";
981 return "(not talking)";
984 static int careful_write(int fd, unsigned char *data, int len, int block)
991 x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
992 res = ioctl(fd, DAHDI_IOMUX, &x);
996 res = write(fd, data, len);
998 if (errno != EAGAIN) {
999 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
1011 static int set_talk_volume(struct ast_conf_user *user, int volume)
1015 /* attempt to make the adjustment in the channel driver;
1016 if successful, don't adjust in the frame reading routine
1018 gain_adjust = gain_map[volume + 5];
1020 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1023 static int set_listen_volume(struct ast_conf_user *user, int volume)
1027 /* attempt to make the adjustment in the channel driver;
1028 if successful, don't adjust in the frame reading routine
1030 gain_adjust = gain_map[volume + 5];
1032 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1035 static void tweak_volume(struct volume *vol, enum volume_action action)
1039 switch (vol->desired) {
1054 switch (vol->desired) {
1070 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
1072 tweak_volume(&user->talk, action);
1073 /* attempt to make the adjustment in the channel driver;
1074 if successful, don't adjust in the frame reading routine
1076 if (!set_talk_volume(user, user->talk.desired))
1077 user->talk.actual = 0;
1079 user->talk.actual = user->talk.desired;
1082 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
1084 tweak_volume(&user->listen, action);
1085 /* attempt to make the adjustment in the channel driver;
1086 if successful, don't adjust in the frame reading routine
1088 if (!set_listen_volume(user, user->listen.desired))
1089 user->listen.actual = 0;
1091 user->listen.actual = user->listen.desired;
1094 static void reset_volumes(struct ast_conf_user *user)
1096 signed char zero_volume = 0;
1098 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
1099 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
1102 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
1104 unsigned char *data;
1108 if (!ast_check_hangup(chan))
1109 res = ast_autoservice_start(chan);
1111 AST_LIST_LOCK(&confs);
1116 len = sizeof(enter);
1120 len = sizeof(leave);
1127 careful_write(conf->fd, data, len, 1);
1130 AST_LIST_UNLOCK(&confs);
1133 ast_autoservice_stop(chan);
1137 * \brief Find or create a conference
1139 * \param confno The conference name/number
1140 * \param pin The regular user pin
1141 * \param pinadmin The admin pin
1142 * \param make Make the conf if it doesn't exist
1143 * \param dynamic Mark the newly created conference as dynamic
1144 * \param refcount How many references to mark on the conference
1145 * \param chan The asterisk channel
1147 * \return A pointer to the conference struct, or NULL if it wasn't found and
1148 * make or dynamic were not set.
1150 static struct ast_conference *build_conf(const char *confno, const char *pin,
1151 const char *pinadmin, int make, int dynamic, int refcount,
1152 const struct ast_channel *chan)
1154 struct ast_conference *cnf;
1155 struct dahdi_confinfo dahdic = { 0, };
1158 AST_LIST_LOCK(&confs);
1160 AST_LIST_TRAVERSE(&confs, cnf, list) {
1161 if (!strcmp(confno, cnf->confno))
1165 if (cnf || (!make && !dynamic))
1168 /* Make a new one */
1169 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
1172 ast_mutex_init(&cnf->playlock);
1173 ast_mutex_init(&cnf->listenlock);
1174 cnf->recordthread = AST_PTHREADT_NULL;
1175 ast_mutex_init(&cnf->recordthreadlock);
1176 cnf->announcethread = AST_PTHREADT_NULL;
1177 ast_mutex_init(&cnf->announcethreadlock);
1178 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
1179 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
1180 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
1181 ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
1183 /* Setup a new dahdi conference */
1185 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1186 cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
1187 if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
1188 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
1196 cnf->dahdiconf = dahdic.confno;
1198 /* Setup a new channel for playback of audio files */
1199 cnf->chan = ast_request("DAHDI", AST_FORMAT_SLINEAR, chan, "pseudo", NULL);
1201 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
1202 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
1204 dahdic.confno = cnf->dahdiconf;
1205 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1206 if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &dahdic)) {
1207 ast_log(LOG_WARNING, "Error setting conference\n");
1209 ast_hangup(cnf->chan);
1219 /* Fill the conference struct */
1220 cnf->start = time(NULL);
1221 cnf->maxusers = 0x7fffffff;
1222 cnf->isdynamic = dynamic ? 1 : 0;
1223 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
1224 AST_LIST_INSERT_HEAD(&confs, cnf, list);
1226 /* Reserve conference number in map */
1227 if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1228 conf_map[confno_int] = 1;
1232 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
1234 AST_LIST_UNLOCK(&confs);
1239 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
1241 static const char * const cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
1243 int len = strlen(word);
1245 struct ast_conference *cnf = NULL;
1246 struct ast_conf_user *usr = NULL;
1247 char *confno = NULL;
1248 char usrno[50] = "";
1249 char *myline, *ret = NULL;
1251 if (pos == 1) { /* Command */
1252 return ast_cli_complete(word, cmds, state);
1253 } else if (pos == 2) { /* Conference Number */
1254 AST_LIST_LOCK(&confs);
1255 AST_LIST_TRAVERSE(&confs, cnf, list) {
1256 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
1261 ret = ast_strdup(ret); /* dup before releasing the lock */
1262 AST_LIST_UNLOCK(&confs);
1264 } else if (pos == 3) {
1265 /* User Number || Conf Command option*/
1266 if (strstr(line, "mute") || strstr(line, "kick")) {
1267 if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
1268 return ast_strdup("all");
1270 AST_LIST_LOCK(&confs);
1272 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
1273 myline = ast_strdupa(line);
1274 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
1275 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
1279 AST_LIST_TRAVERSE(&confs, cnf, list) {
1280 if (!strcmp(confno, cnf->confno))
1285 /* Search for the user */
1286 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
1287 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
1288 if (!strncasecmp(word, usrno, len) && ++which > state)
1292 AST_LIST_UNLOCK(&confs);
1293 return usr ? ast_strdup(usrno) : NULL;
1300 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1302 /* Process the command */
1303 struct ast_conf_user *user;
1304 struct ast_conference *cnf;
1306 int i = 0, total = 0;
1308 struct ast_str *cmdline = NULL;
1309 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
1310 #define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
1314 e->command = "meetme list [concise]";
1316 "Usage: meetme list [concise] <confno> \n"
1317 " List all or a specific conference.\n";
1320 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1323 /* Check for length so no buffer will overflow... */
1324 for (i = 0; i < a->argc; i++) {
1325 if (strlen(a->argv[i]) > 100)
1326 ast_cli(a->fd, "Invalid Arguments.\n");
1329 /* Max confno length */
1330 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1334 if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], "concise"))) {
1335 /* List all the conferences */
1336 int concise = (a->argc == 3 && !strcasecmp(a->argv[2], "concise"));
1338 AST_LIST_LOCK(&confs);
1339 if (AST_LIST_EMPTY(&confs)) {
1341 ast_cli(a->fd, "No active MeetMe conferences.\n");
1343 AST_LIST_UNLOCK(&confs);
1348 ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
1350 AST_LIST_TRAVERSE(&confs, cnf, list) {
1351 if (cnf->markedusers == 0) {
1352 ast_str_set(&cmdline, 0, "N/A ");
1354 ast_str_set(&cmdline, 0, "%4.4d", cnf->markedusers);
1356 hr = (now - cnf->start) / 3600;
1357 min = ((now - cnf->start) % 3600) / 60;
1358 sec = (now - cnf->start) % 60;
1360 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");
1362 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1371 total += cnf->users;
1373 AST_LIST_UNLOCK(&confs);
1375 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1379 } else if (strcmp(a->argv[1], "list") == 0) {
1380 int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
1381 /* List all the users in a conference */
1382 if (AST_LIST_EMPTY(&confs)) {
1384 ast_cli(a->fd, "No active MeetMe conferences.\n");
1389 /* Find the right conference */
1390 AST_LIST_LOCK(&confs);
1391 AST_LIST_TRAVERSE(&confs, cnf, list) {
1392 if (strcmp(cnf->confno, a->argv[2]) == 0) {
1398 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
1399 AST_LIST_UNLOCK(&confs);
1403 /* Show all the users */
1405 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
1406 hr = (now - user->jointime) / 3600;
1407 min = ((now - user->jointime) % 3600) / 60;
1408 sec = (now - user->jointime) % 60;
1410 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1412 S_OR(user->chan->cid.cid_num, "<unknown>"),
1413 S_OR(user->chan->cid.cid_name, "<no name>"),
1415 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "(Admin)" : "",
1416 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "(Listen only)" : "",
1417 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1418 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1419 istalking(user->talking), hr, min, sec);
1421 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1423 S_OR(user->chan->cid.cid_num, ""),
1424 S_OR(user->chan->cid.cid_name, ""),
1426 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "1" : "",
1427 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "1" : "",
1428 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1429 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1430 user->talking, hr, min, sec);
1434 ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
1436 AST_LIST_UNLOCK(&confs);
1442 return CLI_SHOWUSAGE;
1445 ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
1447 admin_exec(NULL, ast_str_buffer(cmdline));
1454 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1456 /* Process the command */
1457 struct ast_str *cmdline = NULL;
1462 e->command = "meetme {lock|unlock|mute|unmute|kick}";
1464 "Usage: meetme (un)lock|(un)mute|kick <confno> <usernumber>\n"
1465 " Executes a command for the conference or on a conferee\n";
1468 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1472 ast_cli(a->fd, "Invalid Arguments.\n");
1473 /* Check for length so no buffer will overflow... */
1474 for (i = 0; i < a->argc; i++) {
1475 if (strlen(a->argv[i]) > 100)
1476 ast_cli(a->fd, "Invalid Arguments.\n");
1479 /* Max confno length */
1480 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1486 return CLI_SHOWUSAGE;
1489 ast_str_set(&cmdline, 0, "%s", a->argv[2]); /* Argv 2: conference number */
1490 if (strstr(a->argv[1], "lock")) {
1491 if (strcmp(a->argv[1], "lock") == 0) {
1493 ast_str_append(&cmdline, 0, ",L");
1496 ast_str_append(&cmdline, 0, ",l");
1498 } else if (strstr(a->argv[1], "mute")) {
1501 return CLI_SHOWUSAGE;
1503 if (strcmp(a->argv[1], "mute") == 0) {
1505 if (strcmp(a->argv[3], "all") == 0) {
1506 ast_str_append(&cmdline, 0, ",N");
1508 ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);
1512 if (strcmp(a->argv[3], "all") == 0) {
1513 ast_str_append(&cmdline, 0, ",n");
1515 ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
1518 } else if (strcmp(a->argv[1], "kick") == 0) {
1521 return CLI_SHOWUSAGE;
1523 if (strcmp(a->argv[3], "all") == 0) {
1525 ast_str_append(&cmdline, 0, ",K");
1527 /* Kick a single user */
1528 ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
1532 return CLI_SHOWUSAGE;
1535 ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
1537 admin_exec(NULL, ast_str_buffer(cmdline));
1543 static const char *sla_hold_str(unsigned int hold_access)
1545 const char *hold = "Unknown";
1547 switch (hold_access) {
1551 case SLA_HOLD_PRIVATE:
1560 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1562 const struct sla_trunk *trunk;
1566 e->command = "sla show trunks";
1568 "Usage: sla show trunks\n"
1569 " This will list all trunks defined in sla.conf\n";
1576 "=============================================================\n"
1577 "=== Configured SLA Trunks ===================================\n"
1578 "=============================================================\n"
1580 AST_RWLIST_RDLOCK(&sla_trunks);
1581 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1582 struct sla_station_ref *station_ref;
1583 char ring_timeout[16] = "(none)";
1584 if (trunk->ring_timeout)
1585 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1586 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1587 "=== Trunk Name: %s\n"
1588 "=== ==> Device: %s\n"
1589 "=== ==> AutoContext: %s\n"
1590 "=== ==> RingTimeout: %s\n"
1591 "=== ==> BargeAllowed: %s\n"
1592 "=== ==> HoldAccess: %s\n"
1593 "=== ==> Stations ...\n",
1594 trunk->name, trunk->device,
1595 S_OR(trunk->autocontext, "(none)"),
1597 trunk->barge_disabled ? "No" : "Yes",
1598 sla_hold_str(trunk->hold_access));
1599 AST_RWLIST_RDLOCK(&sla_stations);
1600 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1601 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
1602 AST_RWLIST_UNLOCK(&sla_stations);
1603 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
1605 AST_RWLIST_UNLOCK(&sla_trunks);
1606 ast_cli(a->fd, "=============================================================\n\n");
1611 static const char *trunkstate2str(enum sla_trunk_state state)
1613 #define S(e) case e: return # e;
1615 S(SLA_TRUNK_STATE_IDLE)
1616 S(SLA_TRUNK_STATE_RINGING)
1617 S(SLA_TRUNK_STATE_UP)
1618 S(SLA_TRUNK_STATE_ONHOLD)
1619 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1621 return "Uknown State";
1625 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1627 const struct sla_station *station;
1631 e->command = "sla show stations";
1633 "Usage: sla show stations\n"
1634 " This will list all stations defined in sla.conf\n";
1641 "=============================================================\n"
1642 "=== Configured SLA Stations =================================\n"
1643 "=============================================================\n"
1645 AST_RWLIST_RDLOCK(&sla_stations);
1646 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1647 struct sla_trunk_ref *trunk_ref;
1648 char ring_timeout[16] = "(none)";
1649 char ring_delay[16] = "(none)";
1650 if (station->ring_timeout) {
1651 snprintf(ring_timeout, sizeof(ring_timeout),
1652 "%u", station->ring_timeout);
1654 if (station->ring_delay) {
1655 snprintf(ring_delay, sizeof(ring_delay),
1656 "%u", station->ring_delay);
1658 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1659 "=== Station Name: %s\n"
1660 "=== ==> Device: %s\n"
1661 "=== ==> AutoContext: %s\n"
1662 "=== ==> RingTimeout: %s\n"
1663 "=== ==> RingDelay: %s\n"
1664 "=== ==> HoldAccess: %s\n"
1665 "=== ==> Trunks ...\n",
1666 station->name, station->device,
1667 S_OR(station->autocontext, "(none)"),
1668 ring_timeout, ring_delay,
1669 sla_hold_str(station->hold_access));
1670 AST_RWLIST_RDLOCK(&sla_trunks);
1671 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1672 if (trunk_ref->ring_timeout) {
1673 snprintf(ring_timeout, sizeof(ring_timeout),
1674 "%u", trunk_ref->ring_timeout);
1676 strcpy(ring_timeout, "(none)");
1677 if (trunk_ref->ring_delay) {
1678 snprintf(ring_delay, sizeof(ring_delay),
1679 "%u", trunk_ref->ring_delay);
1681 strcpy(ring_delay, "(none)");
1682 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
1683 "=== ==> State: %s\n"
1684 "=== ==> RingTimeout: %s\n"
1685 "=== ==> RingDelay: %s\n",
1686 trunk_ref->trunk->name,
1687 trunkstate2str(trunk_ref->state),
1688 ring_timeout, ring_delay);
1690 AST_RWLIST_UNLOCK(&sla_trunks);
1691 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1694 AST_RWLIST_UNLOCK(&sla_stations);
1695 ast_cli(a->fd, "============================================================\n"
1701 static struct ast_cli_entry cli_meetme[] = {
1702 AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
1703 AST_CLI_DEFINE(meetme_show_cmd, "List all or one conference"),
1704 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
1705 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
1708 static void conf_flush(int fd, struct ast_channel *chan)
1712 /* read any frames that may be waiting on the channel
1716 struct ast_frame *f;
1718 /* when no frames are available, this will wait
1719 for 1 millisecond maximum
1721 while (ast_waitfor(chan, 1)) {
1725 else /* channel was hung up or something else happened */
1730 /* flush any data sitting in the pseudo channel */
1731 x = DAHDI_FLUSH_ALL;
1732 if (ioctl(fd, DAHDI_FLUSH, &x))
1733 ast_log(LOG_WARNING, "Error flushing channel\n");
1737 /*! \brief Remove the conference from the list and free it.
1739 We assume that this was called while holding conflock. */
1740 static int conf_free(struct ast_conference *conf)
1743 struct announce_listitem *item;
1745 AST_LIST_REMOVE(&confs, conf, list);
1746 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1748 if (conf->recording == MEETME_RECORD_ACTIVE) {
1749 conf->recording = MEETME_RECORD_TERMINATE;
1750 AST_LIST_UNLOCK(&confs);
1753 AST_LIST_LOCK(&confs);
1754 if (conf->recording == MEETME_RECORD_OFF)
1756 AST_LIST_UNLOCK(&confs);
1760 for (x = 0; x < AST_FRAME_BITS; x++) {
1761 if (conf->transframe[x])
1762 ast_frfree(conf->transframe[x]);
1763 if (conf->transpath[x])
1764 ast_translator_free_path(conf->transpath[x]);
1766 if (conf->announcethread != AST_PTHREADT_NULL) {
1767 ast_mutex_lock(&conf->announcelistlock);
1768 conf->announcethread_stop = 1;
1769 ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
1770 ast_cond_signal(&conf->announcelist_addition);
1771 ast_mutex_unlock(&conf->announcelistlock);
1772 pthread_join(conf->announcethread, NULL);
1774 while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
1775 ast_filedelete(item->namerecloc, NULL);
1778 ast_mutex_destroy(&conf->announcelistlock);
1781 if (conf->origframe)
1782 ast_frfree(conf->origframe);
1784 ast_hangup(conf->lchan);
1786 ast_hangup(conf->chan);
1789 if (conf->recordingfilename) {
1790 ast_free(conf->recordingfilename);
1793 if (conf->recordingformat) {
1794 ast_free(conf->recordingformat);
1796 ast_mutex_destroy(&conf->playlock);
1797 ast_mutex_destroy(&conf->listenlock);
1798 ast_mutex_destroy(&conf->recordthreadlock);
1799 ast_mutex_destroy(&conf->announcethreadlock);
1805 static void conf_queue_dtmf(const struct ast_conference *conf,
1806 const struct ast_conf_user *sender, struct ast_frame *f)
1808 struct ast_conf_user *user;
1810 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1813 if (ast_write(user->chan, f) < 0)
1814 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1818 static void sla_queue_event_full(enum sla_event_type type,
1819 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1821 struct sla_event *event;
1823 if (sla.thread == AST_PTHREADT_NULL) {
1827 if (!(event = ast_calloc(1, sizeof(*event))))
1831 event->trunk_ref = trunk_ref;
1832 event->station = station;
1835 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1839 ast_mutex_lock(&sla.lock);
1840 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1841 ast_cond_signal(&sla.cond);
1842 ast_mutex_unlock(&sla.lock);
1845 static void sla_queue_event_nolock(enum sla_event_type type)
1847 sla_queue_event_full(type, NULL, NULL, 0);
1850 static void sla_queue_event(enum sla_event_type type)
1852 sla_queue_event_full(type, NULL, NULL, 1);
1855 /*! \brief Queue a SLA event from the conference */
1856 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1857 struct ast_conference *conf)
1859 struct sla_station *station;
1860 struct sla_trunk_ref *trunk_ref = NULL;
1863 trunk_name = ast_strdupa(conf->confno);
1864 strsep(&trunk_name, "_");
1865 if (ast_strlen_zero(trunk_name)) {
1866 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1870 AST_RWLIST_RDLOCK(&sla_stations);
1871 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1872 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1873 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1879 AST_RWLIST_UNLOCK(&sla_stations);
1882 ast_debug(1, "Trunk not found for event!\n");
1886 sla_queue_event_full(type, trunk_ref, station, 1);
1889 /*! \brief Decrement reference counts, as incremented by find_conf() */
1890 static int dispose_conf(struct ast_conference *conf)
1895 AST_LIST_LOCK(&confs);
1896 if (ast_atomic_dec_and_test(&conf->refcount)) {
1897 /* Take the conference room number out of an inuse state */
1898 if ((sscanf(conf->confno, "%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
1899 conf_map[confno_int] = 0;
1904 AST_LIST_UNLOCK(&confs);
1909 static int rt_extend_conf(const char *confno)
1911 char currenttime[32];
1915 struct ast_variable *var, *orig_var;
1924 ast_localtime(&now, &tm, NULL);
1925 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1927 var = ast_load_realtime("meetme", "confno",
1928 confno, "startTime<= ", currenttime,
1929 "endtime>= ", currenttime, NULL);
1933 /* Identify the specific RealTime conference */
1935 if (!strcasecmp(var->name, "bookid")) {
1936 ast_copy_string(bookid, var->value, sizeof(bookid));
1938 if (!strcasecmp(var->name, "endtime")) {
1939 ast_copy_string(endtime, var->value, sizeof(endtime));
1944 ast_variables_destroy(orig_var);
1946 ast_strptime(endtime, DATE_FORMAT, &tm);
1947 now = ast_mktime(&tm, NULL);
1949 now.tv_sec += extendby;
1951 ast_localtime(&now, &tm, NULL);
1952 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1953 strcat(currenttime, "0"); /* Seconds needs to be 00 */
1955 var = ast_load_realtime("meetme", "confno",
1956 confno, "startTime<= ", currenttime,
1957 "endtime>= ", currenttime, NULL);
1959 /* If there is no conflict with extending the conference, update the DB */
1961 ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
1962 ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
1967 ast_variables_destroy(var);
1971 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
1975 ast_channel_lock(chan);
1976 original_moh = ast_strdupa(chan->musicclass);
1977 ast_string_field_set(chan, musicclass, musicclass);
1978 ast_channel_unlock(chan);
1980 ast_moh_start(chan, original_moh, NULL);
1982 ast_channel_lock(chan);
1983 ast_string_field_set(chan, musicclass, original_moh);
1984 ast_channel_unlock(chan);
1987 static const char *get_announce_filename(enum announcetypes type)
1991 return "conf-hasleft";
1994 return "conf-hasjoin";
2001 static void *announce_thread(void *data)
2003 struct announce_listitem *current;
2004 struct ast_conference *conf = data;
2006 char filename[PATH_MAX] = "";
2007 AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
2008 AST_LIST_HEAD_INIT_NOLOCK(&local_list);
2010 while (!conf->announcethread_stop) {
2011 ast_mutex_lock(&conf->announcelistlock);
2012 if (conf->announcethread_stop) {
2013 ast_mutex_unlock(&conf->announcelistlock);
2016 if (AST_LIST_EMPTY(&conf->announcelist))
2017 ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
2019 AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
2020 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2022 ast_mutex_unlock(&conf->announcelistlock);
2023 if (conf->announcethread_stop) {
2027 for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
2028 ast_log(LOG_DEBUG, "About to play %s\n", current->namerecloc);
2029 if (!ast_fileexists(current->namerecloc, NULL, NULL))
2031 if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
2032 if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
2033 res = ast_waitstream(current->confchan, "");
2035 ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
2036 if (!ast_streamfile(current->confchan, filename, current->language))
2037 ast_waitstream(current->confchan, "");
2040 if (current->announcetype == CONF_HASLEFT) {
2041 ast_filedelete(current->namerecloc, NULL);
2046 /* thread marked to stop, clean up */
2047 while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
2048 ast_filedelete(current->namerecloc, NULL);
2049 ao2_ref(current, -1);
2054 static int can_write(struct ast_channel *chan, struct ast_flags64 *confflags)
2056 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
2060 return (chan->_state == AST_STATE_UP);
2063 static void send_talking_event(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking)
2065 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalking",
2071 chan->name, chan->uniqueid, conf->confno, user->user_no, talking ? "on" : "off");
2074 static void set_user_talking(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor)
2076 int last_talking = user->talking;
2077 if (last_talking == talking)
2080 user->talking = talking;
2083 /* Check if talking state changed. Take care of -1 which means unmonitored */
2084 int was_talking = (last_talking > 0);
2085 int now_talking = (talking > 0);
2086 if (was_talking != now_talking) {
2087 send_talking_event(chan, conf, user, now_talking);
2092 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struct ast_flags64 *confflags, char *optargs[])
2094 struct ast_conf_user *user = NULL;
2095 struct ast_conf_user *usr = NULL;
2097 struct dahdi_confinfo dahdic, dahdic_empty;
2098 struct ast_frame *f;
2099 struct ast_channel *c;
2100 struct ast_frame fr;
2107 int musiconhold = 0, mohtempstopped = 0;
2110 int currentmarked = 0;
2113 int menu_active = 0;
2114 int menu8_active = 0;
2115 int talkreq_manager = 0;
2116 int using_pseudo = 0;
2121 int announcement_played = 0;
2123 struct ast_dsp *dsp = NULL;
2124 struct ast_app *agi_app;
2125 char *agifile, *mod_speex;
2126 const char *agifiledefault = "conf-background.agi", *tmpvar;
2127 char meetmesecs[30] = "";
2128 char exitcontext[AST_MAX_CONTEXT] = "";
2129 char recordingtmp[AST_MAX_EXTENSION] = "";
2130 char members[10] = "";
2131 int dtmf, opt_waitmarked_timeout = 0;
2133 struct dahdi_bufferinfo bi;
2134 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
2135 char *buf = __buf + AST_FRIENDLY_OFFSET;
2136 char *exitkeys = NULL;
2137 unsigned int calldurationlimit = 0;
2139 long play_warning = 0;
2140 long warning_freq = 0;
2141 const char *warning_sound = NULL;
2142 const char *end_sound = NULL;
2144 long time_left_ms = 0;
2145 struct timeval nexteventts = { 0, };
2147 int setusercount = 0;
2148 int confsilence = 0, totalsilence = 0;
2150 if (!(user = ast_calloc(1, sizeof(*user))))
2153 /* Possible timeout waiting for marked user */
2154 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
2155 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
2156 (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
2157 (opt_waitmarked_timeout > 0)) {
2158 timeout = time(NULL) + opt_waitmarked_timeout;
2161 if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
2162 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
2163 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
2166 if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
2167 char *limit_str, *warning_str, *warnfreq_str;
2170 parse = optargs[OPT_ARG_DURATION_LIMIT];
2171 limit_str = strsep(&parse, ":");
2172 warning_str = strsep(&parse, ":");
2173 warnfreq_str = parse;
2175 timelimit = atol(limit_str);
2177 play_warning = atol(warning_str);
2179 warning_freq = atol(warnfreq_str);
2182 timelimit = play_warning = warning_freq = 0;
2183 warning_sound = NULL;
2184 } else if (play_warning > timelimit) {
2185 if (!warning_freq) {
2188 while (play_warning > timelimit)
2189 play_warning -= warning_freq;
2190 if (play_warning < 1)
2191 play_warning = warning_freq = 0;
2195 ast_channel_lock(chan);
2196 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
2197 var = ast_strdupa(var);
2199 ast_channel_unlock(chan);
2201 warning_sound = var ? var : "timeleft";
2203 ast_channel_lock(chan);
2204 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
2205 var = ast_strdupa(var);
2207 ast_channel_unlock(chan);
2209 end_sound = var ? var : NULL;
2211 /* undo effect of S(x) in case they are both used */
2212 calldurationlimit = 0;
2213 /* more efficient do it like S(x) does since no advanced opts */
2214 if (!play_warning && !end_sound && timelimit) {
2215 calldurationlimit = timelimit / 1000;
2216 timelimit = play_warning = warning_freq = 0;
2218 ast_debug(2, "Limit Data for this call:\n");
2219 ast_debug(2, "- timelimit = %ld\n", timelimit);
2220 ast_debug(2, "- play_warning = %ld\n", play_warning);
2221 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
2222 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
2223 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
2228 if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
2229 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
2230 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
2232 exitkeys = ast_strdupa("#"); /* Default */
2235 if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
2236 if (!conf->recordingfilename) {
2238 ast_channel_lock(chan);
2239 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
2240 conf->recordingfilename = ast_strdup(var);
2242 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
2243 conf->recordingformat = ast_strdup(var);
2245 ast_channel_unlock(chan);
2246 if (!conf->recordingfilename) {
2247 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
2248 conf->recordingfilename = ast_strdup(recordingtmp);
2250 if (!conf->recordingformat) {
2251 conf->recordingformat = ast_strdup("wav");
2253 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
2254 conf->confno, conf->recordingfilename, conf->recordingformat);
2258 ast_mutex_lock(&conf->recordthreadlock);
2259 if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
2260 ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, chan, "pseudo", NULL)))) {
2261 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
2262 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
2264 dahdic.confno = conf->dahdiconf;
2265 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
2266 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
2267 ast_log(LOG_WARNING, "Error starting listen channel\n");
2268 ast_hangup(conf->lchan);
2271 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
2274 ast_mutex_unlock(&conf->recordthreadlock);
2276 ast_mutex_lock(&conf->announcethreadlock);
2277 if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
2278 (ast_test_flag64(confflags, CONFFLAG_INTROUSER) || ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))) {
2279 ast_mutex_init(&conf->announcelistlock);
2280 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2281 ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
2283 ast_mutex_unlock(&conf->announcethreadlock);
2285 time(&user->jointime);
2287 user->timelimit = timelimit;
2288 user->play_warning = play_warning;
2289 user->warning_freq = warning_freq;
2290 user->warning_sound = warning_sound;
2291 user->end_sound = end_sound;
2293 if (calldurationlimit > 0) {
2294 time(&user->kicktime);
2295 user->kicktime = user->kicktime + calldurationlimit;
2298 if (ast_tvzero(user->start_time))
2299 user->start_time = ast_tvnow();
2300 time_left_ms = user->timelimit;
2302 if (user->timelimit) {
2303 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2304 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
2307 if (conf->locked && (!ast_test_flag64(confflags, CONFFLAG_ADMIN))) {
2308 /* Sorry, but this conference is locked! */
2309 if (!ast_streamfile(chan, "conf-locked", chan->language))
2310 ast_waitstream(chan, "");
2314 ast_mutex_lock(&conf->playlock);
2316 if (AST_LIST_EMPTY(&conf->userlist))
2319 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
2321 if (rt_schedule && conf->maxusers)
2322 if (conf->users >= conf->maxusers) {
2323 /* Sorry, but this confernce has reached the participant limit! */
2324 if (!ast_streamfile(chan, "conf-full", chan->language))
2325 ast_waitstream(chan, "");
2326 ast_mutex_unlock(&conf->playlock);
2331 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
2334 user->userflags = *confflags;
2335 user->adminflags = ast_test_flag64(confflags, CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
2336 user->adminflags |= (conf->gmuted) ? ADMINFLAG_MUTED : 0;
2339 ast_mutex_unlock(&conf->playlock);
2341 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
2342 ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))) {
2343 char destdir[PATH_MAX];
2345 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
2347 if (ast_mkdir(destdir, 0777) != 0) {
2348 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
2352 snprintf(user->namerecloc, sizeof(user->namerecloc),
2353 "%s/meetme-username-%s-%d", destdir,
2354 conf->confno, user->user_no);
2355 if (ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))
2356 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
2358 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
2363 ast_mutex_lock(&conf->playlock);
2365 if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER))
2366 conf->markedusers++;
2368 if (rt_log_members) {
2370 snprintf(members, sizeof(members), "%d", conf->users);
2371 ast_realtime_require_field("meetme",
2372 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
2373 "members", RQ_UINTEGER1, strlen(members),
2375 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2379 /* This device changed state now - if this is the first user */
2380 if (conf->users == 1)
2381 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
2383 ast_mutex_unlock(&conf->playlock);
2385 /* return the unique ID of the conference */
2386 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
2388 if (ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT)) {
2389 ast_channel_lock(chan);
2390 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
2391 ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
2392 } else if (!ast_strlen_zero(chan->macrocontext)) {
2393 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
2395 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
2397 ast_channel_unlock(chan);
2400 /* Play an arbitrary intro message */
2401 if (ast_test_flag64(confflags, CONFFLAG_INTROMSG) &&
2402 !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
2403 if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], chan->language)) {
2404 ast_waitstream(chan, "");
2408 if (!ast_test_flag64(confflags, (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
2409 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED))
2410 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
2411 ast_waitstream(chan, "");
2412 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && conf->markedusers == 0)
2413 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
2414 ast_waitstream(chan, "");
2417 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_ANNOUNCEUSERCOUNT) &&
2419 int keepplaying = 1;
2421 if (conf->users == 2) {
2422 if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
2423 res = ast_waitstream(chan, AST_DIGIT_ANY);
2424 ast_stopstream(chan);
2431 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
2432 res = ast_waitstream(chan, AST_DIGIT_ANY);
2433 ast_stopstream(chan);
2440 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2446 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
2447 res = ast_waitstream(chan, AST_DIGIT_ANY);
2448 ast_stopstream(chan);
2457 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
2458 /* We're leaving this alone until the state gets changed to up */
2459 ast_indicate(chan, -1);
2462 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
2463 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
2467 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
2468 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
2472 /* Reduce background noise from each participant */
2473 if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
2474 ast_free(mod_speex);
2475 ast_func_write(chan, "DENOISE(rx)", "on");
2478 retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
2479 user->dahdichannel = !retrydahdi;
2482 origfd = chan->fds[0];
2484 /* open pseudo in non-blocking mode */
2485 fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
2487 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
2491 /* Setup buffering information */
2492 memset(&bi, 0, sizeof(bi));
2493 bi.bufsize = CONF_SIZE / 2;
2494 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
2495 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
2496 bi.numbufs = audio_buffers;
2497 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
2498 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
2503 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
2504 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
2510 /* XXX Make sure we're not running on a pseudo channel XXX */
2514 memset(&dahdic, 0, sizeof(dahdic));
2515 memset(&dahdic_empty, 0, sizeof(dahdic_empty));
2516 /* Check to see if we're in a conference... */
2518 if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
2519 ast_log(LOG_WARNING, "Error getting conference\n");
2523 if (dahdic.confmode) {
2524 /* Whoa, already in a conference... Retry... */
2526 ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
2531 memset(&dahdic, 0, sizeof(dahdic));
2532 /* Add us to the conference */
2534 dahdic.confno = conf->dahdiconf;
2536 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
2537 ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
2538 struct announce_listitem *item;
2539 if (!(item = ao2_alloc(sizeof(*item), NULL)))
2541 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
2542 ast_copy_string(item->language, chan->language, sizeof(item->language));
2543 item->confchan = conf->chan;
2544 item->confusers = conf->users;
2545 item->announcetype = CONF_HASJOIN;
2546 ast_mutex_lock(&conf->announcelistlock);
2547 ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
2548 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
2549 ast_cond_signal(&conf->announcelist_addition);
2550 ast_mutex_unlock(&conf->announcelistlock);
2552 while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
2558 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED && !conf->markedusers))
2559 dahdic.confmode = DAHDI_CONF_CONF;
2560 else if (ast_test_flag64(confflags, CONFFLAG_MONITOR))
2561 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2562 else if (ast_test_flag64(confflags, CONFFLAG_TALKER))
2563 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2565 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2567 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2568 ast_log(LOG_WARNING, "Error setting conference\n");
2572 ast_debug(1, "Placed channel %s in DAHDI conf %d\n", chan->name, conf->dahdiconf);
2575 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeJoin",
2580 "CallerIDnum: %s\r\n"
2581 "CallerIDname: %s\r\n",
2582 chan->name, chan->uniqueid, conf->confno,
2584 S_OR(user->chan->cid.cid_num, "<unknown>"),
2585 S_OR(user->chan->cid.cid_name, "<unknown>")
2590 if (!firstpass && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
2591 !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
2593 if (!ast_test_flag64(confflags, CONFFLAG_QUIET))
2594 if (!ast_test_flag64(confflags, CONFFLAG_WAITMARKED) || (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
2595 (conf->markedusers >= 1))) {
2596 conf_play(chan, conf, ENTER);
2600 conf_flush(fd, chan);
2602 if (!(dsp = ast_dsp_new())) {
2603 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
2607 if (ast_test_flag64(confflags, CONFFLAG_AGI)) {
2608 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
2609 or use default filename of conf-background.agi */
2611 ast_channel_lock(chan);
2612 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
2613 agifile = ast_strdupa(tmpvar);
2615 agifile = ast_strdupa(agifiledefault);
2617 ast_channel_unlock(chan);
2619 if (user->dahdichannel) {
2620 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
2622 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2624 /* Find a pointer to the agi app and execute the script */
2625 agi_app = pbx_findapp("agi");
2627 ret = pbx_exec(chan, agi_app, agifile);
2629 ast_log(LOG_WARNING, "Could not find application (agi)\n");
2632 if (user->dahdichannel) {
2633 /* Remove CONFMUTE mode on DAHDI channel */
2635 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2638 if (user->dahdichannel && ast_test_flag64(confflags, CONFFLAG_STARMENU)) {
2639 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
2641 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2644 int menu_was_active = 0;
2650 if (rt_schedule && conf->endtime) {
2651 char currenttime[32];
2652 long localendtime = 0;
2655 struct ast_variable *var, *origvar;
2658 if (now.tv_sec % 60 == 0) {
2660 ast_localtime(&now, &tm, NULL);
2661 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2662 var = origvar = ast_load_realtime("meetme", "confno",
2663 conf->confno, "starttime <=", currenttime,
2664 "endtime >=", currenttime, NULL);
2666 for ( ; var; var = var->next) {
2667 if (!strcasecmp(var->name, "endtime")) {
2668 struct ast_tm endtime_tm;
2669 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
2670 tmp = ast_mktime(&endtime_tm, NULL);
2671 localendtime = tmp.tv_sec;
2674 ast_variables_destroy(origvar);
2676 /* A conference can be extended from the
2677 Admin/User menu or by an external source */
2678 if (localendtime > conf->endtime){
2679 conf->endtime = localendtime;
2683 if (conf->endtime && (now.tv_sec >= conf->endtime)) {
2684 ast_verbose("Quitting time...\n");
2688 if (!announcement_played && conf->endalert) {
2689 if (now.tv_sec + conf->endalert >= conf->endtime) {
2690 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
2691 ast_waitstream(chan, "");
2692 ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", chan->language);
2693 if (!ast_streamfile(chan, "minutes", chan->language))
2694 ast_waitstream(chan, "");
2695 announcement_played = 1;
2700 announcement_played = 0;
2710 if (user->kicktime && (user->kicktime <= now.tv_sec)) {
2715 if (user->timelimit) {
2716 int minutes = 0, seconds = 0, remain = 0;
2718 to = ast_tvdiff_ms(nexteventts, now);
2722 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
2723 if (time_left_ms < to) {
2727 if (time_left_ms <= 0) {
2728 if (user->end_sound) {
2729 res = ast_streamfile(chan, user->end_sound, chan->language);
2730 res = ast_waitstream(chan, "");
2736 if (time_left_ms >= 5000) {
2738 remain = (time_left_ms + 500) / 1000;
2739 if (remain / 60 >= 1) {
2740 minutes = remain / 60;
2741 seconds = remain % 60;
2746 /* force the time left to round up if appropriate */
2747 if (user->warning_sound && user->play_warning) {
2748 if (!strcmp(user->warning_sound, "timeleft")) {
2750 res = ast_streamfile(chan, "vm-youhave", chan->language);
2751 res = ast_waitstream(chan, "");
2753 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
2754 res = ast_streamfile(chan, "queue-minutes", chan->language);
2755 res = ast_waitstream(chan, "");
2758 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
2759 res = ast_streamfile(chan, "queue-seconds", chan->language);
2760 res = ast_waitstream(chan, "");
2763 res = ast_streamfile(chan, user->warning_sound, chan->language);
2764 res = ast_waitstream(chan, "");
2768 if (user->warning_freq) {
2769 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
2771 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2777 if (timeout && now.tv_sec >= timeout) {
2781 /* if we have just exited from the menu, and the user had a channel-driver
2782 volume adjustment, restore it
2784 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual) {
2785 set_talk_volume(user, user->listen.desired);
2788 menu_was_active = menu_active;
2790 currentmarked = conf->markedusers;
2791 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
2792 ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
2793 ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
2795 if (currentmarked == 1 && conf->users > 1) {
2796 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2797 if (conf->users - 1 == 1) {
2798 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language)) {
2799 ast_waitstream(chan, "");
2802 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language)) {
2803 ast_waitstream(chan, "");
2807 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
2808 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
2809 ast_waitstream(chan, "");
2814 /* Update the struct with the actual confflags */
2815 user->userflags = *confflags;
2817 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
2818 if (currentmarked == 0) {
2819 if (lastmarked != 0) {
2820 if (!ast_test_flag64(confflags, CONFFLAG_QUIET)) {
2821 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language)) {
2822 ast_waitstream(chan, "");
2825 if (ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
2826 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
2831 dahdic.confmode = DAHDI_CONF_CONF;
2832 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2833 ast_log(LOG_WARNING, "Error setting conference\n");