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"
67 <application name="MeetMe" language="en_US">
69 MeetMe conference bridge.
72 <parameter name="confno">
73 <para>The conference number</para>
75 <parameter name="options">
78 <para>Set admin mode.</para>
81 <para>Set marked mode.</para>
84 <para>Run AGI script specified in <variable>MEETME_AGI_BACKGROUND</variable>
85 Default: <literal>conf-background.agi</literal>.</para>
86 <note><para>This does not work with non-DAHDI channels in the same
87 conference).</para></note>
90 <para>Announce user(s) count on joining a conference.</para>
93 <para>Continue in dialplan when kicked out of conference.</para>
96 <para>Dynamically add conference.</para>
99 <para>Dynamically add conference, prompting for a PIN.</para>
102 <para>Select an empty conference.</para>
105 <para>Select an empty pinless conference.</para>
108 <para>Pass DTMF through the conference.</para>
111 <argument name="x" required="true">
112 <para>The file to playback</para>
114 <para>Play an intro announcement in conference.</para>
117 <para>Announce user join/leave with review.</para>
120 <para>Announce user join/leave without review.</para>
123 <para>Set listen only mode (Listen only, no talking).</para>
126 <para>Set initially muted.</para>
128 <option name="M" hasparams="optional">
129 <para>Enable music on hold when the conference has a single caller. Optionally,
130 specify a musiconhold class to use. If one is not provided, it will use the
131 channel's currently set music class, or <literal>default</literal>.</para>
132 <argument name="class" required="true" />
135 <para>Set talker optimization - treats talkers who aren't speaking as
136 being muted, meaning (a) No encode is done on transmission and (b)
137 Received audio that is not registered as talking is omitted causing no
138 buildup in background noise.</para>
140 <option name="p" hasparams="optional">
141 <para>Allow user to exit the conference by pressing <literal>#</literal> (default)
142 or any of the defined keys. If keys contain <literal>*</literal> this will override
143 option <literal>s</literal>. The key used is set to channel variable
144 <variable>MEETME_EXIT_KEY</variable>.</para>
145 <argument name="keys" required="true" />
148 <para>Always prompt for the pin even if it is specified.</para>
151 <para>Quiet mode (don't play enter/leave sounds).</para>
154 <para>Record conference (records as <variable>MEETME_RECORDINGFILE</variable>
155 using format <variable>MEETME_RECORDINGFORMAT</variable>. Default filename is
156 <literal>meetme-conf-rec-${CONFNO}-${UNIQUEID}</literal> and the default format is
160 <para>Present menu (user or admin) when <literal>*</literal> is received
161 (send to menu).</para>
164 <para>Set talk only mode. (Talk only, no listening).</para>
167 <para>Set talker detection (sent to manager interface and meetme list).</para>
169 <option name="W" hasparams="optional">
170 <para>Wait until the marked user enters the conference.</para>
171 <argument name="secs" required="true" />
174 <para>Close the conference when last marked user exits</para>
177 <para>Allow user to exit the conference by entering a valid single digit
178 extension <variable>MEETME_EXIT_CONTEXT</variable> or the current context
179 if that variable is not defined.</para>
182 <para>Do not play message when first person enters</para>
185 <para>Kick the user <replaceable>x</replaceable> seconds <emphasis>after</emphasis> he entered into
186 the conference.</para>
187 <argument name="x" required="true" />
189 <option name="L" argsep=":">
190 <para>Limit the conference to <replaceable>x</replaceable> ms. Play a warning when
191 <replaceable>y</replaceable> ms are left. Repeat the warning every <replaceable>z</replaceable> ms.
192 The following special variables can be used with this option:</para>
194 <variable name="CONF_LIMIT_TIMEOUT_FILE">
195 <para>File to play when time is up.</para>
197 <variable name="CONF_LIMIT_WARNING_FILE">
198 <para>File to play as warning if <replaceable>y</replaceable> is defined. The
199 default is to say the time remaining.</para>
202 <argument name="x" />
203 <argument name="y" />
204 <argument name="z" />
208 <parameter name="pin" />
211 <para>Enters the user into a specified MeetMe conference. If the <replaceable>confno</replaceable>
212 is omitted, the user will be prompted to enter one. User can exit the conference by hangup, or
213 if the <literal>p</literal> option is specified, by pressing <literal>#</literal>.</para>
214 <note><para>The DAHDI kernel modules and at least one hardware driver (or dahdi_dummy)
215 must be present for conferencing to operate properly. In addition, the chan_dahdi channel driver
216 must be loaded for the <literal>i</literal> and <literal>r</literal> options to operate at
220 <ref type="application">MeetMeCount</ref>
221 <ref type="application">MeetMeAdmin</ref>
222 <ref type="application">MeetMeChannelAdmin</ref>
225 <application name="MeetMeCount" language="en_US">
227 MeetMe participant count.
230 <parameter name="confno" required="true">
231 <para>Conference number.</para>
233 <parameter name="var" />
236 <para>Plays back the number of users in the specified MeetMe conference.
237 If <replaceable>var</replaceable> is specified, playback will be skipped and the value
238 will be returned in the variable. Upon application completion, MeetMeCount will hangup
239 the channel, unless priority <literal>n+1</literal> exists, in which case priority progress will
243 <ref type="application">MeetMe</ref>
246 <application name="MeetMeAdmin" language="en_US">
248 MeetMe conference administration.
251 <parameter name="confno" required="true" />
252 <parameter name="command" required="true">
255 <para>Eject last user that joined.</para>
258 <para>Extend conference end time, if scheduled.</para>
261 <para>Kick one user out of conference.</para>
264 <para>Kick all users out of conference.</para>
267 <para>Unlock conference.</para>
270 <para>Lock conference.</para>
273 <para>Unmute one user.</para>
276 <para>Mute one user.</para>
279 <para>Unmute all users in the conference.</para>
282 <para>Mute all non-admin users in the conference.</para>
285 <para>Reset one user's volume settings.</para>
288 <para>Reset all users volume settings.</para>
291 <para>Lower entire conference speaking volume.</para>
294 <para>Raise entire conference speaking volume.</para>
297 <para>Lower one user's talk volume.</para>
300 <para>Raise one user's talk volume.</para>
303 <para>Lower one user's listen volume.</para>
306 <para>Raise one user's listen volume.</para>
309 <para>Lower entire conference listening volume.</para>
312 <para>Raise entire conference listening volume.</para>
316 <parameter name="user" />
319 <para>Run admin <replaceable>command</replaceable> for conference <replaceable>confno</replaceable>.</para>
320 <para>Will additionally set the variable <variable>MEETMEADMINSTATUS</variable> with one of
321 the following values:</para>
323 <variable name="MEETMEADMINSTATUS">
324 <value name="NOPARSE">
327 <value name="NOTFOUND">
328 User specified was not found.
330 <value name="FAILED">
331 Another failure occurred.
334 The operation was completed successfully.
340 <ref type="application">MeetMe</ref>
343 <application name="MeetMeChannelAdmin" language="en_US">
345 MeetMe conference Administration (channel specific).
348 <parameter name="channel" required="true" />
349 <parameter name="command" required="true">
352 <para>Kick the specified user out of the conference he is in.</para>
355 <para>Unmute the specified user.</para>
358 <para>Mute the specified user.</para>
364 <para>Run admin <replaceable>command</replaceable> for a specific
365 <replaceable>channel</replaceable> in any coference.</para>
368 <application name="SLAStation" language="en_US">
370 Shared Line Appearance Station.
373 <parameter name="station" required="true">
374 <para>Station name</para>
378 <para>This application should be executed by an SLA station. The argument depends
379 on how the call was initiated. If the phone was just taken off hook, then the argument
380 <replaceable>station</replaceable> should be just the station name. If the call was
381 initiated by pressing a line key, then the station name should be preceded by an underscore
382 and the trunk name associated with that line button.</para>
383 <para>For example: <literal>station1_line1</literal></para>
384 <para>On exit, this application will set the variable <variable>SLASTATION_STATUS</variable> to
385 one of the following values:</para>
387 <variable name="SLASTATION_STATUS">
388 <value name="FAILURE" />
389 <value name="CONGESTION" />
390 <value name="SUCCESS" />
395 <application name="SLATrunk" language="en_US">
397 Shared Line Appearance Trunk.
400 <parameter name="trunk" required="true">
401 <para>Trunk name</para>
403 <parameter name="options">
405 <option name="M" hasparams="optional">
406 <para>Play back the specified MOH <replaceable>class</replaceable>
407 instead of ringing</para>
408 <argument name="class" required="true" />
414 <para>This application should be executed by an SLA trunk on an inbound call. The channel calling
415 this application should correspond to the SLA trunk with the name <replaceable>trunk</replaceable>
416 that is being passed as an argument.</para>
417 <para>On exit, this application will set the variable <variable>SLATRUNK_STATUS</variable> to
418 one of the following values:</para>
420 <variable name="SLATRUNK_STATUS">
421 <value name="FAILURE" />
422 <value name="SUCCESS" />
423 <value name="UNANSWERED" />
424 <value name="RINGTIMEOUT" />
429 <function name="MEETME_INFO" language="en_US">
431 Query a given conference of various properties.
434 <parameter name="keyword" required="true">
435 <para>Options:</para>
438 <para>Boolean of whether the corresponding conference is locked.</para>
440 <enum name="parties">
441 <para>Number of parties in a given conference</para>
443 <enum name="activity">
444 <para>Duration of conference in seconds.</para>
446 <enum name="dynamic">
447 <para>Boolean of whether the corresponding conference is dynamic.</para>
451 <parameter name="confno" required="true">
452 <para>Conference number to retrieve information from.</para>
457 <ref type="application">MeetMe</ref>
458 <ref type="application">MeetMeCount</ref>
459 <ref type="application">MeetMeAdmin</ref>
460 <ref type="application">MeetMeChannelAdmin</ref>
463 <manager name="MeetmeMute" language="en_US">
468 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
469 <parameter name="Meetme" required="true" />
470 <parameter name="Usernum" required="true" />
475 <manager name="MeetmeUnmute" language="en_US">
477 Unmute a Meetme user.
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="MeetmeList" language="en_US">
489 List participants in a conference.
492 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
493 <parameter name="Conference" required="true">
494 <para>Conference number.</para>
498 <para>Lists all users in a particular MeetMe conference.
499 MeetmeList will follow as separate events, followed by a final event called
500 MeetmeListComplete.</para>
505 #define CONFIG_FILE_NAME "meetme.conf"
506 #define SLA_CONFIG_FILE "sla.conf"
508 /*! each buffer is 20ms, so this is 640ms total */
509 #define DEFAULT_AUDIO_BUFFERS 32
511 /*! String format for scheduled conferences */
512 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
515 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
516 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
517 ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */
518 /*! User has requested to speak */
519 ADMINFLAG_T_REQUEST = (1 << 4),
522 #define MEETME_DELAYDETECTTALK 300
523 #define MEETME_DELAYDETECTENDTALK 1000
525 #define AST_FRAME_BITS 32
532 enum entrance_sound {
537 enum recording_state {
539 MEETME_RECORD_STARTED,
540 MEETME_RECORD_ACTIVE,
541 MEETME_RECORD_TERMINATE
544 #define CONF_SIZE 320
547 /*! user has admin access on the conference */
548 CONFFLAG_ADMIN = (1 << 0),
549 /*! If set the user can only receive audio from the conference */
550 CONFFLAG_MONITOR = (1 << 1),
551 /*! If set asterisk will exit conference when key defined in p() option is pressed */
552 CONFFLAG_KEYEXIT = (1 << 2),
553 /*! If set asterisk will provide a menu to the user when '*' is pressed */
554 CONFFLAG_STARMENU = (1 << 3),
555 /*! If set the use can only send audio to the conference */
556 CONFFLAG_TALKER = (1 << 4),
557 /*! If set there will be no enter or leave sounds */
558 CONFFLAG_QUIET = (1 << 5),
559 /*! If set, when user joins the conference, they will be told the number
560 * of users that are already in */
561 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
562 /*! Set to run AGI Script in Background */
563 CONFFLAG_AGI = (1 << 7),
564 /*! Set to have music on hold when user is alone in conference */
565 CONFFLAG_MOH = (1 << 8),
566 /*! If set the MeetMe will return if all marked with this flag left */
567 CONFFLAG_MARKEDEXIT = (1 << 9),
568 /*! If set, the MeetMe will wait until a marked user enters */
569 CONFFLAG_WAITMARKED = (1 << 10),
570 /*! If set, the MeetMe will exit to the specified context */
571 CONFFLAG_EXIT_CONTEXT = (1 << 11),
572 /*! If set, the user will be marked */
573 CONFFLAG_MARKEDUSER = (1 << 12),
574 /*! If set, user will be ask record name on entry of conference */
575 CONFFLAG_INTROUSER = (1 << 13),
576 /*! If set, the MeetMe will be recorded */
577 CONFFLAG_RECORDCONF = (1<< 14),
578 /*! If set, the user will be monitored if the user is talking or not */
579 CONFFLAG_MONITORTALKER = (1 << 15),
580 CONFFLAG_DYNAMIC = (1 << 16),
581 CONFFLAG_DYNAMICPIN = (1 << 17),
582 CONFFLAG_EMPTY = (1 << 18),
583 CONFFLAG_EMPTYNOPIN = (1 << 19),
584 CONFFLAG_ALWAYSPROMPT = (1 << 20),
585 /*! If set, treat talking users as muted users */
586 CONFFLAG_OPTIMIZETALKER = (1 << 21),
587 /*! If set, won't speak the extra prompt when the first person
588 * enters the conference */
589 CONFFLAG_NOONLYPERSON = (1 << 22),
590 /*! If set, user will be asked to record name on entry of conference
592 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
593 /*! If set, the user will be initially self-muted */
594 CONFFLAG_STARTMUTED = (1 << 24),
595 /*! Pass DTMF through the conference */
596 CONFFLAG_PASS_DTMF = (1 << 25),
597 CONFFLAG_SLA_STATION = (1 << 26),
598 CONFFLAG_SLA_TRUNK = (1 << 27),
599 /*! If set, the user should continue in the dialplan if kicked out */
600 CONFFLAG_KICK_CONTINUE = (1 << 28),
601 CONFFLAG_DURATION_STOP = (1 << 29),
602 CONFFLAG_DURATION_LIMIT = (1 << 30),
603 /*! Do not write any audio to this channel until the state is up. */
604 CONFFLAG_NO_AUDIO_UNTIL_UP = (1 << 31),
607 /* !If set play an intro announcement at start of conference */
608 #define CONFFLAG_INTROMSG ((uint64_t)1 << 32)
611 OPT_ARG_WAITMARKED = 0,
612 OPT_ARG_EXITKEYS = 1,
613 OPT_ARG_DURATION_STOP = 2,
614 OPT_ARG_DURATION_LIMIT = 3,
615 OPT_ARG_MOH_CLASS = 4,
616 OPT_ARG_INTROMSG = 5,
617 OPT_ARG_ARRAY_SIZE = 6,
620 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
621 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
622 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
623 AST_APP_OPTION('b', CONFFLAG_AGI ),
624 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
625 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
626 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
627 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
628 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
629 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
630 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
631 AST_APP_OPTION_ARG('G', CONFFLAG_INTROMSG, OPT_ARG_INTROMSG ),
632 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
633 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
634 AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
635 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
636 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
637 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
638 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
639 AST_APP_OPTION('q', CONFFLAG_QUIET ),
640 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
641 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
642 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
643 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
644 AST_APP_OPTION('t', CONFFLAG_TALKER ),
645 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
646 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
647 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
648 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
649 AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
650 AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
653 static const char * const app = "MeetMe";
654 static const char * const app2 = "MeetMeCount";
655 static const char * const app3 = "MeetMeAdmin";
656 static const char * const app4 = "MeetMeChannelAdmin";
657 static const char * const slastation_app = "SLAStation";
658 static const char * const slatrunk_app = "SLATrunk";
660 /* Lookup RealTime conferences based on confno and current time */
661 static int rt_schedule;
662 static int fuzzystart;
663 static int earlyalert;
667 /*! Log participant count to the RealTime backend */
668 static int rt_log_members;
670 #define MAX_CONFNUM 80
672 #define OPTIONS_LEN 100
679 struct announce_listitem {
680 AST_LIST_ENTRY(announce_listitem) entry;
681 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
682 char language[MAX_LANGUAGE];
683 struct ast_channel *confchan;
685 enum announcetypes announcetype;
688 /*! \brief The MeetMe Conference object */
689 struct ast_conference {
690 ast_mutex_t playlock; /*!< Conference specific lock (players) */
691 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
692 char confno[MAX_CONFNUM]; /*!< Conference */
693 struct ast_channel *chan; /*!< Announcements channel */
694 struct ast_channel *lchan; /*!< Listen/Record channel */
695 int fd; /*!< Announcements fd */
696 int dahdiconf; /*!< DAHDI Conf # */
697 int users; /*!< Number of active users */
698 int markedusers; /*!< Number of marked users */
699 int maxusers; /*!< Participant limit if scheduled */
700 int endalert; /*!< When to play conf ending message */
701 time_t start; /*!< Start time (s) */
702 int refcount; /*!< reference count of usage */
703 enum recording_state recording:2; /*!< recording status */
704 unsigned int isdynamic:1; /*!< Created on the fly? */
705 unsigned int locked:1; /*!< Is the conference locked? */
706 pthread_t recordthread; /*!< thread for recording */
707 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
708 pthread_attr_t attr; /*!< thread attribute */
709 char *recordingfilename; /*!< Filename to record the Conference into */
710 char *recordingformat; /*!< Format to record the Conference in */
711 char pin[MAX_PIN]; /*!< If protected by a PIN */
712 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
714 long endtime; /*!< When to end the conf if scheduled */
715 const char *useropts; /*!< RealTime user flags */
716 const char *adminopts; /*!< RealTime moderator flags */
717 const char *bookid; /*!< RealTime conference id */
718 struct ast_frame *transframe[32];
719 struct ast_frame *origframe;
720 struct ast_trans_pvt *transpath[32];
721 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
722 AST_LIST_ENTRY(ast_conference) list;
723 /* announce_thread related data */
724 pthread_t announcethread;
725 ast_mutex_t announcethreadlock;
726 unsigned int announcethread_stop:1;
727 ast_cond_t announcelist_addition;
728 AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
729 ast_mutex_t announcelistlock;
732 static AST_LIST_HEAD_STATIC(confs, ast_conference);
734 static unsigned int conf_map[1024] = {0, };
737 int desired; /*!< Desired volume adjustment */
738 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
741 /*! \brief The MeetMe User object */
742 struct ast_conf_user {
743 int user_no; /*!< User Number */
744 struct ast_flags64 userflags; /*!< Flags as set in the conference */
745 int adminflags; /*!< Flags set by the Admin */
746 struct ast_channel *chan; /*!< Connected channel */
747 int talking; /*!< Is user talking */
748 int dahdichannel; /*!< Is a DAHDI channel */
749 char usrvalue[50]; /*!< Custom User Value */
750 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
751 time_t jointime; /*!< Time the user joined the conference */
752 time_t kicktime; /*!< Time the user will be kicked from the conference */
753 struct timeval start_time; /*!< Time the user entered into the conference */
754 long timelimit; /*!< Time limit for the user to be in the conference L(x:y:z) */
755 long play_warning; /*!< Play a warning when 'y' ms are left */
756 long warning_freq; /*!< Repeat the warning every 'z' ms */
757 const char *warning_sound; /*!< File to play as warning if 'y' is defined */
758 const char *end_sound; /*!< File to play when time is up. */
760 struct volume listen;
761 AST_LIST_ENTRY(ast_conf_user) list;
764 enum sla_which_trunk_refs {
769 enum sla_trunk_state {
770 SLA_TRUNK_STATE_IDLE,
771 SLA_TRUNK_STATE_RINGING,
773 SLA_TRUNK_STATE_ONHOLD,
774 SLA_TRUNK_STATE_ONHOLD_BYME,
777 enum sla_hold_access {
778 /*! This means that any station can put it on hold, and any station
779 * can retrieve the call from hold. */
781 /*! This means that only the station that put the call on hold may
782 * retrieve it from hold. */
786 struct sla_trunk_ref;
789 AST_RWLIST_ENTRY(sla_station) entry;
790 AST_DECLARE_STRING_FIELDS(
791 AST_STRING_FIELD(name);
792 AST_STRING_FIELD(device);
793 AST_STRING_FIELD(autocontext);
795 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
796 struct ast_dial *dial;
797 /*! Ring timeout for this station, for any trunk. If a ring timeout
798 * is set for a specific trunk on this station, that will take
799 * priority over this value. */
800 unsigned int ring_timeout;
801 /*! Ring delay for this station, for any trunk. If a ring delay
802 * is set for a specific trunk on this station, that will take
803 * priority over this value. */
804 unsigned int ring_delay;
805 /*! This option uses the values in the sla_hold_access enum and sets the
806 * access control type for hold on this station. */
807 unsigned int hold_access:1;
808 /*! Use count for inside sla_station_exec */
809 unsigned int ref_count;
812 struct sla_station_ref {
813 AST_LIST_ENTRY(sla_station_ref) entry;
814 struct sla_station *station;
818 AST_RWLIST_ENTRY(sla_trunk) entry;
819 AST_DECLARE_STRING_FIELDS(
820 AST_STRING_FIELD(name);
821 AST_STRING_FIELD(device);
822 AST_STRING_FIELD(autocontext);
824 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
825 /*! Number of stations that use this trunk */
826 unsigned int num_stations;
827 /*! Number of stations currently on a call with this trunk */
828 unsigned int active_stations;
829 /*! Number of stations that have this trunk on hold. */
830 unsigned int hold_stations;
831 struct ast_channel *chan;
832 unsigned int ring_timeout;
833 /*! If set to 1, no station will be able to join an active call with
835 unsigned int barge_disabled:1;
836 /*! This option uses the values in the sla_hold_access enum and sets the
837 * access control type for hold on this trunk. */
838 unsigned int hold_access:1;
839 /*! Whether this trunk is currently on hold, meaning that once a station
840 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
841 unsigned int on_hold:1;
842 /*! Use count for inside sla_trunk_exec */
843 unsigned int ref_count;
846 struct sla_trunk_ref {
847 AST_LIST_ENTRY(sla_trunk_ref) entry;
848 struct sla_trunk *trunk;
849 enum sla_trunk_state state;
850 struct ast_channel *chan;
851 /*! Ring timeout to use when this trunk is ringing on this specific
852 * station. This takes higher priority than a ring timeout set at
853 * the station level. */
854 unsigned int ring_timeout;
855 /*! Ring delay to use when this trunk is ringing on this specific
856 * station. This takes higher priority than a ring delay set at
857 * the station level. */
858 unsigned int ring_delay;
861 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
862 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
864 static const char sla_registrar[] = "SLA";
866 /*! \brief Event types that can be queued up for the SLA thread */
867 enum sla_event_type {
868 /*! A station has put the call on hold */
870 /*! The state of a dial has changed */
871 SLA_EVENT_DIAL_STATE,
872 /*! The state of a ringing trunk has changed */
873 SLA_EVENT_RINGING_TRUNK,
874 /*! A reload of configuration has been requested */
876 /*! Poke the SLA thread so it can check if it can perform a reload */
877 SLA_EVENT_CHECK_RELOAD,
881 enum sla_event_type type;
882 struct sla_station *station;
883 struct sla_trunk_ref *trunk_ref;
884 AST_LIST_ENTRY(sla_event) entry;
887 /*! \brief A station that failed to be dialed
888 * \note Only used by the SLA thread. */
889 struct sla_failed_station {
890 struct sla_station *station;
891 struct timeval last_try;
892 AST_LIST_ENTRY(sla_failed_station) entry;
895 /*! \brief A trunk that is ringing */
896 struct sla_ringing_trunk {
897 struct sla_trunk *trunk;
898 /*! The time that this trunk started ringing */
899 struct timeval ring_begin;
900 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
901 AST_LIST_ENTRY(sla_ringing_trunk) entry;
904 enum sla_station_hangup {
905 SLA_STATION_HANGUP_NORMAL,
906 SLA_STATION_HANGUP_TIMEOUT,
909 /*! \brief A station that is ringing */
910 struct sla_ringing_station {
911 struct sla_station *station;
912 /*! The time that this station started ringing */
913 struct timeval ring_begin;
914 AST_LIST_ENTRY(sla_ringing_station) entry;
918 * \brief A structure for data used by the sla thread
921 /*! The SLA thread ID */
925 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
926 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
927 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
928 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
930 /*! Attempt to handle CallerID, even though it is known not to work
931 * properly in some situations. */
932 unsigned int attempt_callerid:1;
933 /*! A reload has been requested */
934 unsigned int reload:1;
936 .thread = AST_PTHREADT_NULL,
939 /*! \brief The number of audio buffers to be allocated on pseudo channels
940 * when in a conference */
941 static int audio_buffers;
943 /*! \briefMap 'volume' levels from -5 through +5 into
944 * decibel (dB) settings for channel drivers
945 * \note these are not a straight linear-to-dB
946 * conversion... the numbers have been modified
947 * to give the user a better level of adjustability
949 static const char gain_map[] = {
964 static int admin_exec(struct ast_channel *chan, const char *data);
965 static void *recordthread(void *args);
967 static const char *istalking(int x)
972 return "(unmonitored)";
974 return "(not talking)";
977 static int careful_write(int fd, unsigned char *data, int len, int block)
984 x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
985 res = ioctl(fd, DAHDI_IOMUX, &x);
989 res = write(fd, data, len);
991 if (errno != EAGAIN) {
992 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
1004 static int set_talk_volume(struct ast_conf_user *user, int volume)
1008 /* attempt to make the adjustment in the channel driver;
1009 if successful, don't adjust in the frame reading routine
1011 gain_adjust = gain_map[volume + 5];
1013 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1016 static int set_listen_volume(struct ast_conf_user *user, int volume)
1020 /* attempt to make the adjustment in the channel driver;
1021 if successful, don't adjust in the frame reading routine
1023 gain_adjust = gain_map[volume + 5];
1025 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1028 static void tweak_volume(struct volume *vol, enum volume_action action)
1032 switch (vol->desired) {
1047 switch (vol->desired) {
1063 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
1065 tweak_volume(&user->talk, action);
1066 /* attempt to make the adjustment in the channel driver;
1067 if successful, don't adjust in the frame reading routine
1069 if (!set_talk_volume(user, user->talk.desired))
1070 user->talk.actual = 0;
1072 user->talk.actual = user->talk.desired;
1075 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
1077 tweak_volume(&user->listen, action);
1078 /* attempt to make the adjustment in the channel driver;
1079 if successful, don't adjust in the frame reading routine
1081 if (!set_listen_volume(user, user->listen.desired))
1082 user->listen.actual = 0;
1084 user->listen.actual = user->listen.desired;
1087 static void reset_volumes(struct ast_conf_user *user)
1089 signed char zero_volume = 0;
1091 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
1092 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
1095 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
1097 unsigned char *data;
1101 if (!ast_check_hangup(chan))
1102 res = ast_autoservice_start(chan);
1104 AST_LIST_LOCK(&confs);
1109 len = sizeof(enter);
1113 len = sizeof(leave);
1120 careful_write(conf->fd, data, len, 1);
1123 AST_LIST_UNLOCK(&confs);
1126 ast_autoservice_stop(chan);
1130 * \brief Find or create a conference
1132 * \param confno The conference name/number
1133 * \param pin The regular user pin
1134 * \param pinadmin The admin pin
1135 * \param make Make the conf if it doesn't exist
1136 * \param dynamic Mark the newly created conference as dynamic
1137 * \param refcount How many references to mark on the conference
1138 * \param chan The asterisk channel
1140 * \return A pointer to the conference struct, or NULL if it wasn't found and
1141 * make or dynamic were not set.
1143 static struct ast_conference *build_conf(const char *confno, const char *pin,
1144 const char *pinadmin, int make, int dynamic, int refcount,
1145 const struct ast_channel *chan)
1147 struct ast_conference *cnf;
1148 struct dahdi_confinfo dahdic = { 0, };
1151 AST_LIST_LOCK(&confs);
1153 AST_LIST_TRAVERSE(&confs, cnf, list) {
1154 if (!strcmp(confno, cnf->confno))
1158 if (cnf || (!make && !dynamic))
1161 /* Make a new one */
1162 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
1165 ast_mutex_init(&cnf->playlock);
1166 ast_mutex_init(&cnf->listenlock);
1167 cnf->recordthread = AST_PTHREADT_NULL;
1168 ast_mutex_init(&cnf->recordthreadlock);
1169 cnf->announcethread = AST_PTHREADT_NULL;
1170 ast_mutex_init(&cnf->announcethreadlock);
1171 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
1172 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
1173 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
1174 ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
1176 /* Setup a new dahdi conference */
1178 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1179 cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
1180 if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
1181 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
1189 cnf->dahdiconf = dahdic.confno;
1191 /* Setup a new channel for playback of audio files */
1192 cnf->chan = ast_request("DAHDI", AST_FORMAT_SLINEAR, chan, "pseudo", NULL);
1194 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
1195 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
1197 dahdic.confno = cnf->dahdiconf;
1198 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1199 if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &dahdic)) {
1200 ast_log(LOG_WARNING, "Error setting conference\n");
1202 ast_hangup(cnf->chan);
1212 /* Fill the conference struct */
1213 cnf->start = time(NULL);
1214 cnf->maxusers = 0x7fffffff;
1215 cnf->isdynamic = dynamic ? 1 : 0;
1216 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
1217 AST_LIST_INSERT_HEAD(&confs, cnf, list);
1219 /* Reserve conference number in map */
1220 if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1221 conf_map[confno_int] = 1;
1225 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
1227 AST_LIST_UNLOCK(&confs);
1232 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
1234 static const char * const cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
1236 int len = strlen(word);
1238 struct ast_conference *cnf = NULL;
1239 struct ast_conf_user *usr = NULL;
1240 char *confno = NULL;
1241 char usrno[50] = "";
1242 char *myline, *ret = NULL;
1244 if (pos == 1) { /* Command */
1245 return ast_cli_complete(word, cmds, state);
1246 } else if (pos == 2) { /* Conference Number */
1247 AST_LIST_LOCK(&confs);
1248 AST_LIST_TRAVERSE(&confs, cnf, list) {
1249 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
1254 ret = ast_strdup(ret); /* dup before releasing the lock */
1255 AST_LIST_UNLOCK(&confs);
1257 } else if (pos == 3) {
1258 /* User Number || Conf Command option*/
1259 if (strstr(line, "mute") || strstr(line, "kick")) {
1260 if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
1261 return ast_strdup("all");
1263 AST_LIST_LOCK(&confs);
1265 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
1266 myline = ast_strdupa(line);
1267 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
1268 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
1272 AST_LIST_TRAVERSE(&confs, cnf, list) {
1273 if (!strcmp(confno, cnf->confno))
1278 /* Search for the user */
1279 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
1280 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
1281 if (!strncasecmp(word, usrno, len) && ++which > state)
1285 AST_LIST_UNLOCK(&confs);
1286 return usr ? ast_strdup(usrno) : NULL;
1293 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1295 /* Process the command */
1296 struct ast_conf_user *user;
1297 struct ast_conference *cnf;
1299 int i = 0, total = 0;
1301 struct ast_str *cmdline = NULL;
1302 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
1303 #define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
1307 e->command = "meetme list [concise]";
1309 "Usage: meetme list [concise] <confno> \n"
1310 " List all or a specific conference.\n";
1313 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1316 /* Check for length so no buffer will overflow... */
1317 for (i = 0; i < a->argc; i++) {
1318 if (strlen(a->argv[i]) > 100)
1319 ast_cli(a->fd, "Invalid Arguments.\n");
1322 /* Max confno length */
1323 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1327 if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], "concise"))) {
1328 /* List all the conferences */
1329 int concise = (a->argc == 3 && !strcasecmp(a->argv[2], "concise"));
1331 AST_LIST_LOCK(&confs);
1332 if (AST_LIST_EMPTY(&confs)) {
1334 ast_cli(a->fd, "No active MeetMe conferences.\n");
1336 AST_LIST_UNLOCK(&confs);
1341 ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
1343 AST_LIST_TRAVERSE(&confs, cnf, list) {
1344 if (cnf->markedusers == 0) {
1345 ast_str_set(&cmdline, 0, "N/A ");
1347 ast_str_set(&cmdline, 0, "%4.4d", cnf->markedusers);
1349 hr = (now - cnf->start) / 3600;
1350 min = ((now - cnf->start) % 3600) / 60;
1351 sec = (now - cnf->start) % 60;
1353 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");
1355 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1364 total += cnf->users;
1366 AST_LIST_UNLOCK(&confs);
1368 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1372 } else if (strcmp(a->argv[1], "list") == 0) {
1373 int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
1374 /* List all the users in a conference */
1375 if (AST_LIST_EMPTY(&confs)) {
1377 ast_cli(a->fd, "No active MeetMe conferences.\n");
1382 /* Find the right conference */
1383 AST_LIST_LOCK(&confs);
1384 AST_LIST_TRAVERSE(&confs, cnf, list) {
1385 if (strcmp(cnf->confno, a->argv[2]) == 0) {
1391 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
1392 AST_LIST_UNLOCK(&confs);
1396 /* Show all the users */
1398 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
1399 hr = (now - user->jointime) / 3600;
1400 min = ((now - user->jointime) % 3600) / 60;
1401 sec = (now - user->jointime) % 60;
1403 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1405 S_OR(user->chan->cid.cid_num, "<unknown>"),
1406 S_OR(user->chan->cid.cid_name, "<no name>"),
1408 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "(Admin)" : "",
1409 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "(Listen only)" : "",
1410 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1411 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1412 istalking(user->talking), hr, min, sec);
1414 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1416 S_OR(user->chan->cid.cid_num, ""),
1417 S_OR(user->chan->cid.cid_name, ""),
1419 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "1" : "",
1420 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "1" : "",
1421 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1422 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1423 user->talking, hr, min, sec);
1427 ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
1429 AST_LIST_UNLOCK(&confs);
1435 return CLI_SHOWUSAGE;
1438 ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
1440 admin_exec(NULL, ast_str_buffer(cmdline));
1447 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1449 /* Process the command */
1450 struct ast_str *cmdline = NULL;
1455 e->command = "meetme {lock|unlock|mute|unmute|kick}";
1457 "Usage: meetme (un)lock|(un)mute|kick <confno> <usernumber>\n"
1458 " Executes a command for the conference or on a conferee\n";
1461 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1465 ast_cli(a->fd, "Invalid Arguments.\n");
1466 /* Check for length so no buffer will overflow... */
1467 for (i = 0; i < a->argc; i++) {
1468 if (strlen(a->argv[i]) > 100)
1469 ast_cli(a->fd, "Invalid Arguments.\n");
1472 /* Max confno length */
1473 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1479 return CLI_SHOWUSAGE;
1482 ast_str_set(&cmdline, 0, "%s", a->argv[2]); /* Argv 2: conference number */
1483 if (strstr(a->argv[1], "lock")) {
1484 if (strcmp(a->argv[1], "lock") == 0) {
1486 ast_str_append(&cmdline, 0, ",L");
1489 ast_str_append(&cmdline, 0, ",l");
1491 } else if (strstr(a->argv[1], "mute")) {
1494 return CLI_SHOWUSAGE;
1496 if (strcmp(a->argv[1], "mute") == 0) {
1498 if (strcmp(a->argv[3], "all") == 0) {
1499 ast_str_append(&cmdline, 0, ",N");
1501 ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);
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]);
1511 } else if (strcmp(a->argv[1], "kick") == 0) {
1514 return CLI_SHOWUSAGE;
1516 if (strcmp(a->argv[3], "all") == 0) {
1518 ast_str_append(&cmdline, 0, ",K");
1520 /* Kick a single user */
1521 ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
1525 return CLI_SHOWUSAGE;
1528 ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
1530 admin_exec(NULL, ast_str_buffer(cmdline));
1536 static const char *sla_hold_str(unsigned int hold_access)
1538 const char *hold = "Unknown";
1540 switch (hold_access) {
1544 case SLA_HOLD_PRIVATE:
1553 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1555 const struct sla_trunk *trunk;
1559 e->command = "sla show trunks";
1561 "Usage: sla show trunks\n"
1562 " This will list all trunks defined in sla.conf\n";
1569 "=============================================================\n"
1570 "=== Configured SLA Trunks ===================================\n"
1571 "=============================================================\n"
1573 AST_RWLIST_RDLOCK(&sla_trunks);
1574 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1575 struct sla_station_ref *station_ref;
1576 char ring_timeout[16] = "(none)";
1577 if (trunk->ring_timeout)
1578 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1579 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1580 "=== Trunk Name: %s\n"
1581 "=== ==> Device: %s\n"
1582 "=== ==> AutoContext: %s\n"
1583 "=== ==> RingTimeout: %s\n"
1584 "=== ==> BargeAllowed: %s\n"
1585 "=== ==> HoldAccess: %s\n"
1586 "=== ==> Stations ...\n",
1587 trunk->name, trunk->device,
1588 S_OR(trunk->autocontext, "(none)"),
1590 trunk->barge_disabled ? "No" : "Yes",
1591 sla_hold_str(trunk->hold_access));
1592 AST_RWLIST_RDLOCK(&sla_stations);
1593 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1594 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
1595 AST_RWLIST_UNLOCK(&sla_stations);
1596 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
1598 AST_RWLIST_UNLOCK(&sla_trunks);
1599 ast_cli(a->fd, "=============================================================\n\n");
1604 static const char *trunkstate2str(enum sla_trunk_state state)
1606 #define S(e) case e: return # e;
1608 S(SLA_TRUNK_STATE_IDLE)
1609 S(SLA_TRUNK_STATE_RINGING)
1610 S(SLA_TRUNK_STATE_UP)
1611 S(SLA_TRUNK_STATE_ONHOLD)
1612 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1614 return "Uknown State";
1618 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1620 const struct sla_station *station;
1624 e->command = "sla show stations";
1626 "Usage: sla show stations\n"
1627 " This will list all stations defined in sla.conf\n";
1634 "=============================================================\n"
1635 "=== Configured SLA Stations =================================\n"
1636 "=============================================================\n"
1638 AST_RWLIST_RDLOCK(&sla_stations);
1639 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1640 struct sla_trunk_ref *trunk_ref;
1641 char ring_timeout[16] = "(none)";
1642 char ring_delay[16] = "(none)";
1643 if (station->ring_timeout) {
1644 snprintf(ring_timeout, sizeof(ring_timeout),
1645 "%u", station->ring_timeout);
1647 if (station->ring_delay) {
1648 snprintf(ring_delay, sizeof(ring_delay),
1649 "%u", station->ring_delay);
1651 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1652 "=== Station Name: %s\n"
1653 "=== ==> Device: %s\n"
1654 "=== ==> AutoContext: %s\n"
1655 "=== ==> RingTimeout: %s\n"
1656 "=== ==> RingDelay: %s\n"
1657 "=== ==> HoldAccess: %s\n"
1658 "=== ==> Trunks ...\n",
1659 station->name, station->device,
1660 S_OR(station->autocontext, "(none)"),
1661 ring_timeout, ring_delay,
1662 sla_hold_str(station->hold_access));
1663 AST_RWLIST_RDLOCK(&sla_trunks);
1664 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1665 if (trunk_ref->ring_timeout) {
1666 snprintf(ring_timeout, sizeof(ring_timeout),
1667 "%u", trunk_ref->ring_timeout);
1669 strcpy(ring_timeout, "(none)");
1670 if (trunk_ref->ring_delay) {
1671 snprintf(ring_delay, sizeof(ring_delay),
1672 "%u", trunk_ref->ring_delay);
1674 strcpy(ring_delay, "(none)");
1675 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
1676 "=== ==> State: %s\n"
1677 "=== ==> RingTimeout: %s\n"
1678 "=== ==> RingDelay: %s\n",
1679 trunk_ref->trunk->name,
1680 trunkstate2str(trunk_ref->state),
1681 ring_timeout, ring_delay);
1683 AST_RWLIST_UNLOCK(&sla_trunks);
1684 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1687 AST_RWLIST_UNLOCK(&sla_stations);
1688 ast_cli(a->fd, "============================================================\n"
1694 static struct ast_cli_entry cli_meetme[] = {
1695 AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
1696 AST_CLI_DEFINE(meetme_show_cmd, "List all or one conference"),
1697 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
1698 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
1701 static void conf_flush(int fd, struct ast_channel *chan)
1705 /* read any frames that may be waiting on the channel
1709 struct ast_frame *f;
1711 /* when no frames are available, this will wait
1712 for 1 millisecond maximum
1714 while (ast_waitfor(chan, 1)) {
1718 else /* channel was hung up or something else happened */
1723 /* flush any data sitting in the pseudo channel */
1724 x = DAHDI_FLUSH_ALL;
1725 if (ioctl(fd, DAHDI_FLUSH, &x))
1726 ast_log(LOG_WARNING, "Error flushing channel\n");
1730 /*! \brief Remove the conference from the list and free it.
1732 We assume that this was called while holding conflock. */
1733 static int conf_free(struct ast_conference *conf)
1736 struct announce_listitem *item;
1738 AST_LIST_REMOVE(&confs, conf, list);
1739 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1741 if (conf->recording == MEETME_RECORD_ACTIVE) {
1742 conf->recording = MEETME_RECORD_TERMINATE;
1743 AST_LIST_UNLOCK(&confs);
1746 AST_LIST_LOCK(&confs);
1747 if (conf->recording == MEETME_RECORD_OFF)
1749 AST_LIST_UNLOCK(&confs);
1753 for (x = 0; x < AST_FRAME_BITS; x++) {
1754 if (conf->transframe[x])
1755 ast_frfree(conf->transframe[x]);
1756 if (conf->transpath[x])
1757 ast_translator_free_path(conf->transpath[x]);
1759 if (conf->announcethread != AST_PTHREADT_NULL) {
1760 ast_mutex_lock(&conf->announcelistlock);
1761 conf->announcethread_stop = 1;
1762 ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
1763 ast_cond_signal(&conf->announcelist_addition);
1764 ast_mutex_unlock(&conf->announcelistlock);
1765 pthread_join(conf->announcethread, NULL);
1767 while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
1768 ast_filedelete(item->namerecloc, NULL);
1771 ast_mutex_destroy(&conf->announcelistlock);
1773 if (conf->origframe)
1774 ast_frfree(conf->origframe);
1776 ast_hangup(conf->lchan);
1778 ast_hangup(conf->chan);
1781 if (conf->recordingfilename) {
1782 ast_free(conf->recordingfilename);
1784 if (conf->recordingformat) {
1785 ast_free(conf->recordingformat);
1787 ast_mutex_destroy(&conf->playlock);
1788 ast_mutex_destroy(&conf->listenlock);
1789 ast_mutex_destroy(&conf->recordthreadlock);
1790 ast_mutex_destroy(&conf->announcethreadlock);
1796 static void conf_queue_dtmf(const struct ast_conference *conf,
1797 const struct ast_conf_user *sender, struct ast_frame *f)
1799 struct ast_conf_user *user;
1801 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1804 if (ast_write(user->chan, f) < 0)
1805 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1809 static void sla_queue_event_full(enum sla_event_type type,
1810 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1812 struct sla_event *event;
1814 if (sla.thread == AST_PTHREADT_NULL) {
1818 if (!(event = ast_calloc(1, sizeof(*event))))
1822 event->trunk_ref = trunk_ref;
1823 event->station = station;
1826 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1830 ast_mutex_lock(&sla.lock);
1831 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1832 ast_cond_signal(&sla.cond);
1833 ast_mutex_unlock(&sla.lock);
1836 static void sla_queue_event_nolock(enum sla_event_type type)
1838 sla_queue_event_full(type, NULL, NULL, 0);
1841 static void sla_queue_event(enum sla_event_type type)
1843 sla_queue_event_full(type, NULL, NULL, 1);
1846 /*! \brief Queue a SLA event from the conference */
1847 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1848 struct ast_conference *conf)
1850 struct sla_station *station;
1851 struct sla_trunk_ref *trunk_ref = NULL;
1854 trunk_name = ast_strdupa(conf->confno);
1855 strsep(&trunk_name, "_");
1856 if (ast_strlen_zero(trunk_name)) {
1857 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1861 AST_RWLIST_RDLOCK(&sla_stations);
1862 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1863 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1864 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1870 AST_RWLIST_UNLOCK(&sla_stations);
1873 ast_debug(1, "Trunk not found for event!\n");
1877 sla_queue_event_full(type, trunk_ref, station, 1);
1880 /*! \brief Decrement reference counts, as incremented by find_conf() */
1881 static int dispose_conf(struct ast_conference *conf)
1886 AST_LIST_LOCK(&confs);
1887 if (ast_atomic_dec_and_test(&conf->refcount)) {
1888 /* Take the conference room number out of an inuse state */
1889 if ((sscanf(conf->confno, "%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
1890 conf_map[confno_int] = 0;
1895 AST_LIST_UNLOCK(&confs);
1900 static int rt_extend_conf(const char *confno)
1902 char currenttime[32];
1906 struct ast_variable *var, *orig_var;
1915 ast_localtime(&now, &tm, NULL);
1916 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1918 var = ast_load_realtime("meetme", "confno",
1919 confno, "startTime<= ", currenttime,
1920 "endtime>= ", currenttime, NULL);
1924 /* Identify the specific RealTime conference */
1926 if (!strcasecmp(var->name, "bookid")) {
1927 ast_copy_string(bookid, var->value, sizeof(bookid));
1929 if (!strcasecmp(var->name, "endtime")) {
1930 ast_copy_string(endtime, var->value, sizeof(endtime));
1935 ast_variables_destroy(orig_var);
1937 ast_strptime(endtime, DATE_FORMAT, &tm);
1938 now = ast_mktime(&tm, NULL);
1940 now.tv_sec += extendby;
1942 ast_localtime(&now, &tm, NULL);
1943 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1944 strcat(currenttime, "0"); /* Seconds needs to be 00 */
1946 var = ast_load_realtime("meetme", "confno",
1947 confno, "startTime<= ", currenttime,
1948 "endtime>= ", currenttime, NULL);
1950 /* If there is no conflict with extending the conference, update the DB */
1952 ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
1953 ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
1958 ast_variables_destroy(var);
1962 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
1966 ast_channel_lock(chan);
1967 original_moh = ast_strdupa(chan->musicclass);
1968 ast_string_field_set(chan, musicclass, musicclass);
1969 ast_channel_unlock(chan);
1971 ast_moh_start(chan, original_moh, NULL);
1973 ast_channel_lock(chan);
1974 ast_string_field_set(chan, musicclass, original_moh);
1975 ast_channel_unlock(chan);
1978 static const char *get_announce_filename(enum announcetypes type)
1982 return "conf-hasleft";
1985 return "conf-hasjoin";
1992 static void *announce_thread(void *data)
1994 struct announce_listitem *current;
1995 struct ast_conference *conf = data;
1997 char filename[PATH_MAX] = "";
1998 AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
1999 AST_LIST_HEAD_INIT_NOLOCK(&local_list);
2001 while (!conf->announcethread_stop) {
2002 ast_mutex_lock(&conf->announcelistlock);
2003 if (conf->announcethread_stop) {
2004 ast_mutex_unlock(&conf->announcelistlock);
2007 if (AST_LIST_EMPTY(&conf->announcelist))
2008 ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
2010 AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
2011 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2013 ast_mutex_unlock(&conf->announcelistlock);
2014 if (conf->announcethread_stop) {
2018 for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
2019 ast_log(LOG_DEBUG, "About to play %s\n", current->namerecloc);
2020 if (!ast_fileexists(current->namerecloc, NULL, NULL))
2022 if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
2023 if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
2024 res = ast_waitstream(current->confchan, "");
2026 ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
2027 if (!ast_streamfile(current->confchan, filename, current->language))
2028 ast_waitstream(current->confchan, "");
2031 if (current->announcetype == CONF_HASLEFT) {
2032 ast_filedelete(current->namerecloc, NULL);
2037 /* thread marked to stop, clean up */
2038 while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
2039 ast_filedelete(current->namerecloc, NULL);
2040 ao2_ref(current, -1);
2045 static int can_write(struct ast_channel *chan, struct ast_flags64 *confflags)
2047 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
2051 return (chan->_state == AST_STATE_UP);
2054 static void send_talking_event(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking)
2056 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalking",
2062 chan->name, chan->uniqueid, conf->confno, user->user_no, talking ? "on" : "off");
2065 static void set_user_talking(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor)
2067 int last_talking = user->talking;
2068 if (last_talking == talking)
2071 user->talking = talking;
2074 /* Check if talking state changed. Take care of -1 which means unmonitored */
2075 int was_talking = (last_talking > 0);
2076 int now_talking = (talking > 0);
2077 if (was_talking != now_talking) {
2078 send_talking_event(chan, conf, user, now_talking);
2083 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struct ast_flags64 *confflags, char *optargs[])
2085 struct ast_conf_user *user = NULL;
2086 struct ast_conf_user *usr = NULL;
2088 struct dahdi_confinfo dahdic, dahdic_empty;
2089 struct ast_frame *f;
2090 struct ast_channel *c;
2091 struct ast_frame fr;
2098 int musiconhold = 0, mohtempstopped = 0;
2101 int currentmarked = 0;
2104 int menu_active = 0;
2105 int talkreq_manager = 0;
2106 int using_pseudo = 0;
2111 int announcement_played = 0;
2113 struct ast_dsp *dsp = NULL;
2114 struct ast_app *agi_app;
2115 char *agifile, *mod_speex;
2116 const char *agifiledefault = "conf-background.agi", *tmpvar;
2117 char meetmesecs[30] = "";
2118 char exitcontext[AST_MAX_CONTEXT] = "";
2119 char recordingtmp[AST_MAX_EXTENSION] = "";
2120 char members[10] = "";
2121 int dtmf, opt_waitmarked_timeout = 0;
2123 struct dahdi_bufferinfo bi;
2124 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
2125 char *buf = __buf + AST_FRIENDLY_OFFSET;
2126 char *exitkeys = NULL;
2127 unsigned int calldurationlimit = 0;
2129 long play_warning = 0;
2130 long warning_freq = 0;
2131 const char *warning_sound = NULL;
2132 const char *end_sound = NULL;
2134 long time_left_ms = 0;
2135 struct timeval nexteventts = { 0, };
2137 int setusercount = 0;
2138 int confsilence = 0, totalsilence = 0;
2140 if (!(user = ast_calloc(1, sizeof(*user))))
2143 /* Possible timeout waiting for marked user */
2144 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
2145 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
2146 (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
2147 (opt_waitmarked_timeout > 0)) {
2148 timeout = time(NULL) + opt_waitmarked_timeout;
2151 if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
2152 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
2153 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
2156 if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
2157 char *limit_str, *warning_str, *warnfreq_str;
2160 parse = optargs[OPT_ARG_DURATION_LIMIT];
2161 limit_str = strsep(&parse, ":");
2162 warning_str = strsep(&parse, ":");
2163 warnfreq_str = parse;
2165 timelimit = atol(limit_str);
2167 play_warning = atol(warning_str);
2169 warning_freq = atol(warnfreq_str);
2172 timelimit = play_warning = warning_freq = 0;
2173 warning_sound = NULL;
2174 } else if (play_warning > timelimit) {
2175 if (!warning_freq) {
2178 while (play_warning > timelimit)
2179 play_warning -= warning_freq;
2180 if (play_warning < 1)
2181 play_warning = warning_freq = 0;
2185 ast_channel_lock(chan);
2186 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
2187 var = ast_strdupa(var);
2189 ast_channel_unlock(chan);
2191 warning_sound = var ? var : "timeleft";
2193 ast_channel_lock(chan);
2194 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
2195 var = ast_strdupa(var);
2197 ast_channel_unlock(chan);
2199 end_sound = var ? var : NULL;
2201 /* undo effect of S(x) in case they are both used */
2202 calldurationlimit = 0;
2203 /* more efficient do it like S(x) does since no advanced opts */
2204 if (!play_warning && !end_sound && timelimit) {
2205 calldurationlimit = timelimit / 1000;
2206 timelimit = play_warning = warning_freq = 0;
2208 ast_debug(2, "Limit Data for this call:\n");
2209 ast_debug(2, "- timelimit = %ld\n", timelimit);
2210 ast_debug(2, "- play_warning = %ld\n", play_warning);
2211 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
2212 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
2213 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
2218 if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
2219 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
2220 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
2222 exitkeys = ast_strdupa("#"); /* Default */
2225 if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
2226 if (!conf->recordingfilename) {
2228 ast_channel_lock(chan);
2229 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
2230 conf->recordingfilename = ast_strdup(var);
2232 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
2233 conf->recordingformat = ast_strdup(var);
2235 ast_channel_unlock(chan);
2236 if (!conf->recordingfilename) {
2237 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
2238 conf->recordingfilename = ast_strdup(recordingtmp);
2240 if (!conf->recordingformat) {
2241 conf->recordingformat = ast_strdup("wav");
2243 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
2244 conf->confno, conf->recordingfilename, conf->recordingformat);
2248 ast_mutex_lock(&conf->recordthreadlock);
2249 if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
2250 ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, chan, "pseudo", NULL)))) {
2251 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
2252 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
2254 dahdic.confno = conf->dahdiconf;
2255 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
2256 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
2257 ast_log(LOG_WARNING, "Error starting listen channel\n");
2258 ast_hangup(conf->lchan);
2261 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
2264 ast_mutex_unlock(&conf->recordthreadlock);
2266 ast_mutex_lock(&conf->announcethreadlock);
2267 if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
2268 (ast_test_flag64(confflags, CONFFLAG_INTROUSER) || ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))) {
2269 ast_mutex_init(&conf->announcelistlock);
2270 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2271 ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
2273 ast_mutex_unlock(&conf->announcethreadlock);
2275 time(&user->jointime);
2277 user->timelimit = timelimit;
2278 user->play_warning = play_warning;
2279 user->warning_freq = warning_freq;
2280 user->warning_sound = warning_sound;
2281 user->end_sound = end_sound;
2283 if (calldurationlimit > 0) {
2284 time(&user->kicktime);
2285 user->kicktime = user->kicktime + calldurationlimit;
2288 if (ast_tvzero(user->start_time))
2289 user->start_time = ast_tvnow();
2290 time_left_ms = user->timelimit;
2292 if (user->timelimit) {
2293 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2294 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
2297 if (conf->locked && (!ast_test_flag64(confflags, CONFFLAG_ADMIN))) {
2298 /* Sorry, but this conference is locked! */
2299 if (!ast_streamfile(chan, "conf-locked", chan->language))
2300 ast_waitstream(chan, "");
2304 ast_mutex_lock(&conf->playlock);
2306 if (AST_LIST_EMPTY(&conf->userlist))
2309 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
2311 if (rt_schedule && conf->maxusers)
2312 if (conf->users >= conf->maxusers) {
2313 /* Sorry, but this confernce has reached the participant limit! */
2314 if (!ast_streamfile(chan, "conf-full", chan->language))
2315 ast_waitstream(chan, "");
2316 ast_mutex_unlock(&conf->playlock);
2321 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
2324 user->userflags = *confflags;
2325 user->adminflags = ast_test_flag64(confflags, CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
2328 ast_mutex_unlock(&conf->playlock);
2330 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
2331 ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))) {
2332 char destdir[PATH_MAX];
2334 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
2336 if (ast_mkdir(destdir, 0777) != 0) {
2337 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
2341 snprintf(user->namerecloc, sizeof(user->namerecloc),
2342 "%s/meetme-username-%s-%d", destdir,
2343 conf->confno, user->user_no);
2344 if (ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))
2345 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
2347 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
2352 ast_mutex_lock(&conf->playlock);
2354 if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER))
2355 conf->markedusers++;
2357 if (rt_log_members) {
2359 snprintf(members, sizeof(members), "%d", conf->users);
2360 ast_realtime_require_field("meetme",
2361 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
2362 "members", RQ_UINTEGER1, strlen(members),
2364 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2368 /* This device changed state now - if this is the first user */
2369 if (conf->users == 1)
2370 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
2372 ast_mutex_unlock(&conf->playlock);
2374 /* return the unique ID of the conference */
2375 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
2377 if (ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT)) {
2378 ast_channel_lock(chan);
2379 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
2380 ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
2381 } else if (!ast_strlen_zero(chan->macrocontext)) {
2382 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
2384 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
2386 ast_channel_unlock(chan);
2389 /* Play an arbitrary intro message */
2390 if (ast_test_flag64(confflags, CONFFLAG_INTROMSG) &&
2391 !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
2392 if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], chan->language)) {
2393 ast_waitstream(chan, "");
2397 if (!ast_test_flag64(confflags, (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
2398 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED))
2399 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
2400 ast_waitstream(chan, "");
2401 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && conf->markedusers == 0)
2402 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
2403 ast_waitstream(chan, "");
2406 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_ANNOUNCEUSERCOUNT) &&
2408 int keepplaying = 1;
2410 if (conf->users == 2) {
2411 if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
2412 res = ast_waitstream(chan, AST_DIGIT_ANY);
2413 ast_stopstream(chan);
2420 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
2421 res = ast_waitstream(chan, AST_DIGIT_ANY);
2422 ast_stopstream(chan);
2429 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2435 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
2436 res = ast_waitstream(chan, AST_DIGIT_ANY);
2437 ast_stopstream(chan);
2446 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
2447 /* We're leaving this alone until the state gets changed to up */
2448 ast_indicate(chan, -1);
2451 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
2452 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
2456 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
2457 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
2461 /* Reduce background noise from each participant */
2462 if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
2463 ast_free(mod_speex);
2464 ast_func_write(chan, "DENOISE(rx)", "on");
2467 retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
2468 user->dahdichannel = !retrydahdi;
2471 origfd = chan->fds[0];
2473 /* open pseudo in non-blocking mode */
2474 fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
2476 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
2480 /* Setup buffering information */
2481 memset(&bi, 0, sizeof(bi));
2482 bi.bufsize = CONF_SIZE / 2;
2483 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
2484 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
2485 bi.numbufs = audio_buffers;
2486 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
2487 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
2492 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
2493 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
2499 /* XXX Make sure we're not running on a pseudo channel XXX */
2503 memset(&dahdic, 0, sizeof(dahdic));
2504 memset(&dahdic_empty, 0, sizeof(dahdic_empty));
2505 /* Check to see if we're in a conference... */
2507 if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
2508 ast_log(LOG_WARNING, "Error getting conference\n");
2512 if (dahdic.confmode) {
2513 /* Whoa, already in a conference... Retry... */
2515 ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
2520 memset(&dahdic, 0, sizeof(dahdic));
2521 /* Add us to the conference */
2523 dahdic.confno = conf->dahdiconf;
2525 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
2526 ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
2527 struct announce_listitem *item;
2528 if (!(item = ao2_alloc(sizeof(*item), NULL)))
2530 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
2531 ast_copy_string(item->language, chan->language, sizeof(item->language));
2532 item->confchan = conf->chan;
2533 item->confusers = conf->users;
2534 item->announcetype = CONF_HASJOIN;
2535 ast_mutex_lock(&conf->announcelistlock);
2536 ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
2537 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
2538 ast_cond_signal(&conf->announcelist_addition);
2539 ast_mutex_unlock(&conf->announcelistlock);
2541 while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
2547 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED && !conf->markedusers))
2548 dahdic.confmode = DAHDI_CONF_CONF;
2549 else if (ast_test_flag64(confflags, CONFFLAG_MONITOR))
2550 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2551 else if (ast_test_flag64(confflags, CONFFLAG_TALKER))
2552 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2554 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2556 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2557 ast_log(LOG_WARNING, "Error setting conference\n");
2561 ast_debug(1, "Placed channel %s in DAHDI conf %d\n", chan->name, conf->dahdiconf);
2564 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeJoin",
2569 "CallerIDnum: %s\r\n"
2570 "CallerIDname: %s\r\n",
2571 chan->name, chan->uniqueid, conf->confno,
2573 S_OR(user->chan->cid.cid_num, "<unknown>"),
2574 S_OR(user->chan->cid.cid_name, "<unknown>")
2579 if (!firstpass && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
2580 !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
2582 if (!ast_test_flag64(confflags, CONFFLAG_QUIET))
2583 if (!ast_test_flag64(confflags, CONFFLAG_WAITMARKED) || (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
2584 (conf->markedusers >= 1))) {
2585 conf_play(chan, conf, ENTER);
2589 conf_flush(fd, chan);
2591 if (!(dsp = ast_dsp_new())) {
2592 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
2596 if (ast_test_flag64(confflags, CONFFLAG_AGI)) {
2597 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
2598 or use default filename of conf-background.agi */
2600 ast_channel_lock(chan);
2601 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
2602 agifile = ast_strdupa(tmpvar);
2604 agifile = ast_strdupa(agifiledefault);
2606 ast_channel_unlock(chan);
2608 if (user->dahdichannel) {
2609 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
2611 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2613 /* Find a pointer to the agi app and execute the script */
2614 agi_app = pbx_findapp("agi");
2616 ret = pbx_exec(chan, agi_app, agifile);
2618 ast_log(LOG_WARNING, "Could not find application (agi)\n");
2621 if (user->dahdichannel) {
2622 /* Remove CONFMUTE mode on DAHDI channel */
2624 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2627 if (user->dahdichannel && ast_test_flag64(confflags, CONFFLAG_STARMENU)) {
2628 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
2630 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2633 int menu_was_active = 0;
2639 if (rt_schedule && conf->endtime) {
2640 char currenttime[32];
2641 long localendtime = 0;
2644 struct ast_variable *var, *origvar;
2647 if (now.tv_sec % 60 == 0) {
2649 ast_localtime(&now, &tm, NULL);
2650 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2651 var = origvar = ast_load_realtime("meetme", "confno",
2652 conf->confno, "starttime <=", currenttime,
2653 "endtime >=", currenttime, NULL);
2655 for ( ; var; var = var->next) {
2656 if (!strcasecmp(var->name, "endtime")) {
2657 struct ast_tm endtime_tm;
2658 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
2659 tmp = ast_mktime(&endtime_tm, NULL);
2660 localendtime = tmp.tv_sec;
2663 ast_variables_destroy(origvar);
2665 /* A conference can be extended from the
2666 Admin/User menu or by an external source */
2667 if (localendtime > conf->endtime){
2668 conf->endtime = localendtime;
2672 if (conf->endtime && (now.tv_sec >= conf->endtime)) {
2673 ast_verbose("Quitting time...\n");
2677 if (!announcement_played && conf->endalert) {
2678 if (now.tv_sec + conf->endalert >= conf->endtime) {
2679 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
2680 ast_waitstream(chan, "");
2681 ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", chan->language);
2682 if (!ast_streamfile(chan, "minutes", chan->language))
2683 ast_waitstream(chan, "");
2684 announcement_played = 1;
2689 announcement_played = 0;
2699 if (user->kicktime && (user->kicktime <= now.tv_sec)) {
2704 if (user->timelimit) {
2705 int minutes = 0, seconds = 0, remain = 0;
2707 to = ast_tvdiff_ms(nexteventts, now);
2711 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
2712 if (time_left_ms < to) {
2716 if (time_left_ms <= 0) {
2717 if (user->end_sound) {
2718 res = ast_streamfile(chan, user->end_sound, chan->language);
2719 res = ast_waitstream(chan, "");
2725 if (time_left_ms >= 5000) {
2727 remain = (time_left_ms + 500) / 1000;
2728 if (remain / 60 >= 1) {
2729 minutes = remain / 60;
2730 seconds = remain % 60;
2735 /* force the time left to round up if appropriate */
2736 if (user->warning_sound && user->play_warning) {
2737 if (!strcmp(user->warning_sound, "timeleft")) {
2739 res = ast_streamfile(chan, "vm-youhave", chan->language);
2740 res = ast_waitstream(chan, "");
2742 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
2743 res = ast_streamfile(chan, "queue-minutes", chan->language);
2744 res = ast_waitstream(chan, "");
2747 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
2748 res = ast_streamfile(chan, "queue-seconds", chan->language);
2749 res = ast_waitstream(chan, "");
2752 res = ast_streamfile(chan, user->warning_sound, chan->language);
2753 res = ast_waitstream(chan, "");
2757 if (user->warning_freq) {
2758 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
2760 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2766 if (timeout && now.tv_sec >= timeout) {
2770 /* if we have just exited from the menu, and the user had a channel-driver
2771 volume adjustment, restore it
2773 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual) {
2774 set_talk_volume(user, user->listen.desired);
2777 menu_was_active = menu_active;
2779 currentmarked = conf->markedusers;
2780 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
2781 ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
2782 ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
2784 if (currentmarked == 1 && conf->users > 1) {
2785 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2786 if (conf->users - 1 == 1) {
2787 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language)) {
2788 ast_waitstream(chan, "");
2791 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language)) {
2792 ast_waitstream(chan, "");
2796 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
2797 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
2798 ast_waitstream(chan, "");
2803 /* Update the struct with the actual confflags */
2804 user->userflags = *confflags;
2806 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
2807 if (currentmarked == 0) {
2808 if (lastmarked != 0) {
2809 if (!ast_test_flag64(confflags, CONFFLAG_QUIET)) {
2810 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language)) {
2811 ast_waitstream(chan, "");
2814 if (ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
2815 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
2820 dahdic.confmode = DAHDI_CONF_CONF;
2821 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2822 ast_log(LOG_WARNING, "Error setting conference\n");
2828 if (!musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
2829 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2832 } else if (currentmarked >= 1 && lastmarked == 0) {