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 <para>Announce user join/leave with review.</para>
114 <para>Announce user join/leave without review.</para>
117 <para>Set listen only mode (Listen only, no talking).</para>
120 <para>Set initially muted.</para>
122 <option name="M" hasparams="optional">
123 <para>Enable music on hold when the conference has a single caller. Optionally,
124 specify a musiconhold class to use. If one is not provided, it will use the
125 channel's currently set music class, or <literal>default</literal>.</para>
126 <argument name="class" required="true" />
129 <para>Set talker optimization - treats talkers who aren't speaking as
130 being muted, meaning (a) No encode is done on transmission and (b)
131 Received audio that is not registered as talking is omitted causing no
132 buildup in background noise.</para>
134 <option name="p" hasparams="optional">
135 <para>Allow user to exit the conference by pressing <literal>#</literal> (default)
136 or any of the defined keys. If keys contain <literal>*</literal> this will override
137 option <literal>s</literal>. The key used is set to channel variable
138 <variable>MEETME_EXIT_KEY</variable>.</para>
139 <argument name="keys" required="true" />
142 <para>Always prompt for the pin even if it is specified.</para>
145 <para>Quiet mode (don't play enter/leave sounds).</para>
148 <para>Record conference (records as <variable>MEETME_RECORDINGFILE</variable>
149 using format <variable>MEETME_RECORDINGFORMAT</variable>. Default filename is
150 <literal>meetme-conf-rec-${CONFNO}-${UNIQUEID}</literal> and the default format is
154 <para>Present menu (user or admin) when <literal>*</literal> is received
155 (send to menu).</para>
158 <para>Set talk only mode. (Talk only, no listening).</para>
161 <para>Set talker detection (sent to manager interface and meetme list).</para>
163 <option name="W" hasparams="optional">
164 <para>Wait until the marked user enters the conference.</para>
165 <argument name="secs" required="true" />
168 <para>Close the conference when last marked user exits</para>
171 <para>Allow user to exit the conference by entering a valid single digit
172 extension <variable>MEETME_EXIT_CONTEXT</variable> or the current context
173 if that variable is not defined.</para>
176 <para>Do not play message when first person enters</para>
179 <para>Kick the user <replaceable>x</replaceable> seconds <emphasis>after</emphasis> he entered into
180 the conference.</para>
181 <argument name="x" required="true" />
183 <option name="L" argsep=":">
184 <para>Limit the conference to <replaceable>x</replaceable> ms. Play a warning when
185 <replaceable>y</replaceable> ms are left. Repeat the warning every <replaceable>z</replaceable> ms.
186 The following special variables can be used with this option:</para>
188 <variable name="CONF_LIMIT_TIMEOUT_FILE">
189 <para>File to play when time is up.</para>
191 <variable name="CONF_LIMIT_WARNING_FILE">
192 <para>File to play as warning if <replaceable>y</replaceable> is defined. The
193 default is to say the time remaining.</para>
196 <argument name="x" />
197 <argument name="y" />
198 <argument name="z" />
202 <parameter name="pin" />
205 <para>Enters the user into a specified MeetMe conference. If the <replaceable>confno</replaceable>
206 is omitted, the user will be prompted to enter one. User can exit the conference by hangup, or
207 if the <literal>p</literal> option is specified, by pressing <literal>#</literal>.</para>
208 <note><para>The DAHDI kernel modules and at least one hardware driver (or dahdi_dummy)
209 must be present for conferencing to operate properly. In addition, the chan_dahdi channel driver
210 must be loaded for the <literal>i</literal> and <literal>r</literal> options to operate at
214 <ref type="application">MeetMeCount</ref>
215 <ref type="application">MeetMeAdmin</ref>
216 <ref type="application">MeetMeChannelAdmin</ref>
219 <application name="MeetMeCount" language="en_US">
221 MeetMe participant count.
224 <parameter name="confno" required="true">
225 <para>Conference number.</para>
227 <parameter name="var" />
230 <para>Plays back the number of users in the specified MeetMe conference.
231 If <replaceable>var</replaceable> is specified, playback will be skipped and the value
232 will be returned in the variable. Upon application completion, MeetMeCount will hangup
233 the channel, unless priority <literal>n+1</literal> exists, in which case priority progress will
237 <ref type="application">MeetMe</ref>
240 <application name="MeetMeAdmin" language="en_US">
242 MeetMe conference administration.
245 <parameter name="confno" required="true" />
246 <parameter name="command" required="true">
249 <para>Eject last user that joined.</para>
252 <para>Extend conference end time, if scheduled.</para>
255 <para>Kick one user out of conference.</para>
258 <para>Kick all users out of conference.</para>
261 <para>Unlock conference.</para>
264 <para>Lock conference.</para>
267 <para>Unmute one user.</para>
270 <para>Mute one user.</para>
273 <para>Unmute all users in the conference.</para>
276 <para>Mute all non-admin users in the conference.</para>
279 <para>Reset one user's volume settings.</para>
282 <para>Reset all users volume settings.</para>
285 <para>Lower entire conference speaking volume.</para>
288 <para>Raise entire conference speaking volume.</para>
291 <para>Lower one user's talk volume.</para>
294 <para>Raise one user's talk volume.</para>
297 <para>Lower one user's listen volume.</para>
300 <para>Raise one user's listen volume.</para>
303 <para>Lower entire conference listening volume.</para>
306 <para>Raise entire conference listening volume.</para>
310 <parameter name="user" />
313 <para>Run admin <replaceable>command</replaceable> for conference <replaceable>confno</replaceable>.</para>
314 <para>Will additionally set the variable <variable>MEETMEADMINSTATUS</variable> with one of
315 the following values:</para>
317 <variable name="MEETMEADMINSTATUS">
318 <value name="NOPARSE">
321 <value name="NOTFOUND">
322 User specified was not found.
324 <value name="FAILED">
325 Another failure occurred.
328 The operation was completed successfully.
334 <ref type="application">MeetMe</ref>
337 <application name="MeetMeChannelAdmin" language="en_US">
339 MeetMe conference Administration (channel specific).
342 <parameter name="channel" required="true" />
343 <parameter name="command" required="true">
346 <para>Kick the specified user out of the conference he is in.</para>
349 <para>Unmute the specified user.</para>
352 <para>Mute the specified user.</para>
358 <para>Run admin <replaceable>command</replaceable> for a specific
359 <replaceable>channel</replaceable> in any coference.</para>
362 <application name="SLAStation" language="en_US">
364 Shared Line Appearance Station.
367 <parameter name="station" required="true">
368 <para>Station name</para>
372 <para>This application should be executed by an SLA station. The argument depends
373 on how the call was initiated. If the phone was just taken off hook, then the argument
374 <replaceable>station</replaceable> should be just the station name. If the call was
375 initiated by pressing a line key, then the station name should be preceded by an underscore
376 and the trunk name associated with that line button.</para>
377 <para>For example: <literal>station1_line1</literal></para>
378 <para>On exit, this application will set the variable <variable>SLASTATION_STATUS</variable> to
379 one of the following values:</para>
381 <variable name="SLASTATION_STATUS">
382 <value name="FAILURE" />
383 <value name="CONGESTION" />
384 <value name="SUCCESS" />
389 <application name="SLATrunk" language="en_US">
391 Shared Line Appearance Trunk.
394 <parameter name="trunk" required="true">
395 <para>Trunk name</para>
397 <parameter name="options">
399 <option name="M" hasparams="optional">
400 <para>Play back the specified MOH <replaceable>class</replaceable>
401 instead of ringing</para>
402 <argument name="class" required="true" />
408 <para>This application should be executed by an SLA trunk on an inbound call. The channel calling
409 this application should correspond to the SLA trunk with the name <replaceable>trunk</replaceable>
410 that is being passed as an argument.</para>
411 <para>On exit, this application will set the variable <variable>SLATRUNK_STATUS</variable> to
412 one of the following values:</para>
414 <variable name="SLATRUNK_STATUS">
415 <value name="FAILURE" />
416 <value name="SUCCESS" />
417 <value name="UNANSWERED" />
418 <value name="RINGTIMEOUT" />
425 #define CONFIG_FILE_NAME "meetme.conf"
426 #define SLA_CONFIG_FILE "sla.conf"
428 /*! each buffer is 20ms, so this is 640ms total */
429 #define DEFAULT_AUDIO_BUFFERS 32
431 /*! String format for scheduled conferences */
432 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
435 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
436 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
437 ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */
438 /*! User has requested to speak */
439 ADMINFLAG_T_REQUEST = (1 << 4),
442 #define MEETME_DELAYDETECTTALK 300
443 #define MEETME_DELAYDETECTENDTALK 1000
445 #define AST_FRAME_BITS 32
452 enum entrance_sound {
457 enum recording_state {
459 MEETME_RECORD_STARTED,
460 MEETME_RECORD_ACTIVE,
461 MEETME_RECORD_TERMINATE
464 #define CONF_SIZE 320
467 /*! user has admin access on the conference */
468 CONFFLAG_ADMIN = (1 << 0),
469 /*! If set the user can only receive audio from the conference */
470 CONFFLAG_MONITOR = (1 << 1),
471 /*! If set asterisk will exit conference when key defined in p() option is pressed */
472 CONFFLAG_KEYEXIT = (1 << 2),
473 /*! If set asterisk will provide a menu to the user when '*' is pressed */
474 CONFFLAG_STARMENU = (1 << 3),
475 /*! If set the use can only send audio to the conference */
476 CONFFLAG_TALKER = (1 << 4),
477 /*! If set there will be no enter or leave sounds */
478 CONFFLAG_QUIET = (1 << 5),
479 /*! If set, when user joins the conference, they will be told the number
480 * of users that are already in */
481 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
482 /*! Set to run AGI Script in Background */
483 CONFFLAG_AGI = (1 << 7),
484 /*! Set to have music on hold when user is alone in conference */
485 CONFFLAG_MOH = (1 << 8),
486 /*! If set the MeetMe will return if all marked with this flag left */
487 CONFFLAG_MARKEDEXIT = (1 << 9),
488 /*! If set, the MeetMe will wait until a marked user enters */
489 CONFFLAG_WAITMARKED = (1 << 10),
490 /*! If set, the MeetMe will exit to the specified context */
491 CONFFLAG_EXIT_CONTEXT = (1 << 11),
492 /*! If set, the user will be marked */
493 CONFFLAG_MARKEDUSER = (1 << 12),
494 /*! If set, user will be ask record name on entry of conference */
495 CONFFLAG_INTROUSER = (1 << 13),
496 /*! If set, the MeetMe will be recorded */
497 CONFFLAG_RECORDCONF = (1<< 14),
498 /*! If set, the user will be monitored if the user is talking or not */
499 CONFFLAG_MONITORTALKER = (1 << 15),
500 CONFFLAG_DYNAMIC = (1 << 16),
501 CONFFLAG_DYNAMICPIN = (1 << 17),
502 CONFFLAG_EMPTY = (1 << 18),
503 CONFFLAG_EMPTYNOPIN = (1 << 19),
504 CONFFLAG_ALWAYSPROMPT = (1 << 20),
505 /*! If set, won't speak the extra prompt when the first person
506 * enters the conference */
507 CONFFLAG_NOONLYPERSON = (1 << 22),
508 /*! If set, user will be asked to record name on entry of conference
510 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
511 /*! If set, the user will be initially self-muted */
512 CONFFLAG_STARTMUTED = (1 << 24),
513 /*! Pass DTMF through the conference */
514 CONFFLAG_PASS_DTMF = (1 << 25),
515 CONFFLAG_SLA_STATION = (1 << 26),
516 CONFFLAG_SLA_TRUNK = (1 << 27),
517 /*! If set, the user should continue in the dialplan if kicked out */
518 CONFFLAG_KICK_CONTINUE = (1 << 28),
519 CONFFLAG_DURATION_STOP = (1 << 29),
520 CONFFLAG_DURATION_LIMIT = (1 << 30),
524 OPT_ARG_WAITMARKED = 0,
525 OPT_ARG_EXITKEYS = 1,
526 OPT_ARG_DURATION_STOP = 2,
527 OPT_ARG_DURATION_LIMIT = 3,
528 OPT_ARG_MOH_CLASS = 4,
529 OPT_ARG_ARRAY_SIZE = 5,
532 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
533 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
534 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
535 AST_APP_OPTION('b', CONFFLAG_AGI ),
536 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
537 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
538 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
539 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
540 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
541 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
542 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
543 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
544 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
545 AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
546 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
547 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
548 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
549 AST_APP_OPTION('q', CONFFLAG_QUIET ),
550 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
551 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
552 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
553 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
554 AST_APP_OPTION('t', CONFFLAG_TALKER ),
555 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
556 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
557 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
558 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
559 AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
560 AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
563 static const char *app = "MeetMe";
564 static const char *app2 = "MeetMeCount";
565 static const char *app3 = "MeetMeAdmin";
566 static const char *app4 = "MeetMeChannelAdmin";
567 static const char *slastation_app = "SLAStation";
568 static const char *slatrunk_app = "SLATrunk";
570 /* Lookup RealTime conferences based on confno and current time */
571 static int rt_schedule;
572 static int fuzzystart;
573 static int earlyalert;
577 /* Log participant count to the RealTime backend */
578 static int rt_log_members;
580 #define MAX_CONFNUM 80
582 #define OPTIONS_LEN 32
589 struct announce_listitem {
590 AST_LIST_ENTRY(announce_listitem) entry;
591 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
592 char language[MAX_LANGUAGE];
593 struct ast_channel *confchan;
595 enum announcetypes announcetype;
598 /*! \brief The MeetMe Conference object */
599 struct ast_conference {
600 ast_mutex_t playlock; /*!< Conference specific lock (players) */
601 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
602 char confno[MAX_CONFNUM]; /*!< Conference */
603 struct ast_channel *chan; /*!< Announcements channel */
604 struct ast_channel *lchan; /*!< Listen/Record channel */
605 int fd; /*!< Announcements fd */
606 int dahdiconf; /*!< DAHDI Conf # */
607 int users; /*!< Number of active users */
608 int markedusers; /*!< Number of marked users */
609 int maxusers; /*!< Participant limit if scheduled */
610 int endalert; /*!< When to play conf ending message */
611 time_t start; /*!< Start time (s) */
612 int refcount; /*!< reference count of usage */
613 enum recording_state recording:2; /*!< recording status */
614 unsigned int isdynamic:1; /*!< Created on the fly? */
615 unsigned int locked:1; /*!< Is the conference locked? */
616 pthread_t recordthread; /*!< thread for recording */
617 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
618 pthread_attr_t attr; /*!< thread attribute */
619 char *recordingfilename; /*!< Filename to record the Conference into */
620 char *recordingformat; /*!< Format to record the Conference in */
621 char pin[MAX_PIN]; /*!< If protected by a PIN */
622 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
624 long endtime; /*!< When to end the conf if scheduled */
625 const char *useropts; /*!< RealTime user flags */
626 const char *adminopts; /*!< RealTime moderator flags */
627 const char *bookid; /*!< RealTime conference id */
628 struct ast_frame *transframe[32];
629 struct ast_frame *origframe;
630 struct ast_trans_pvt *transpath[32];
631 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
632 AST_LIST_ENTRY(ast_conference) list;
633 /* announce_thread related data */
634 pthread_t announcethread;
635 ast_mutex_t announcethreadlock;
636 unsigned int announcethread_stop:1;
637 ast_cond_t announcelist_addition;
638 AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
639 ast_mutex_t announcelistlock;
642 static AST_LIST_HEAD_STATIC(confs, ast_conference);
644 static unsigned int conf_map[1024] = {0, };
647 int desired; /*!< Desired volume adjustment */
648 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
651 /*! \brief The MeetMe User object */
652 struct ast_conf_user {
653 int user_no; /*!< User Number */
654 int userflags; /*!< Flags as set in the conference */
655 int adminflags; /*!< Flags set by the Admin */
656 struct ast_channel *chan; /*!< Connected channel */
657 int talking; /*!< Is user talking */
658 int dahdichannel; /*!< Is a DAHDI channel */
659 char usrvalue[50]; /*!< Custom User Value */
660 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
661 time_t jointime; /*!< Time the user joined the conference */
662 time_t kicktime; /*!< Time the user will be kicked from the conference */
663 struct timeval start_time; /*!< Time the user entered into the conference */
664 long timelimit; /*!< Time limit for the user to be in the conference L(x:y:z) */
665 long play_warning; /*!< Play a warning when 'y' ms are left */
666 long warning_freq; /*!< Repeat the warning every 'z' ms */
667 const char *warning_sound; /*!< File to play as warning if 'y' is defined */
668 const char *end_sound; /*!< File to play when time is up. */
670 struct volume listen;
671 AST_LIST_ENTRY(ast_conf_user) list;
674 enum sla_which_trunk_refs {
679 enum sla_trunk_state {
680 SLA_TRUNK_STATE_IDLE,
681 SLA_TRUNK_STATE_RINGING,
683 SLA_TRUNK_STATE_ONHOLD,
684 SLA_TRUNK_STATE_ONHOLD_BYME,
687 enum sla_hold_access {
688 /*! This means that any station can put it on hold, and any station
689 * can retrieve the call from hold. */
691 /*! This means that only the station that put the call on hold may
692 * retrieve it from hold. */
696 struct sla_trunk_ref;
699 AST_RWLIST_ENTRY(sla_station) entry;
700 AST_DECLARE_STRING_FIELDS(
701 AST_STRING_FIELD(name);
702 AST_STRING_FIELD(device);
703 AST_STRING_FIELD(autocontext);
705 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
706 struct ast_dial *dial;
707 /*! Ring timeout for this station, for any trunk. If a ring timeout
708 * is set for a specific trunk on this station, that will take
709 * priority over this value. */
710 unsigned int ring_timeout;
711 /*! Ring delay for this station, for any trunk. If a ring delay
712 * is set for a specific trunk on this station, that will take
713 * priority over this value. */
714 unsigned int ring_delay;
715 /*! This option uses the values in the sla_hold_access enum and sets the
716 * access control type for hold on this station. */
717 unsigned int hold_access:1;
718 /*! Use count for inside sla_station_exec */
719 unsigned int ref_count;
722 struct sla_station_ref {
723 AST_LIST_ENTRY(sla_station_ref) entry;
724 struct sla_station *station;
728 AST_RWLIST_ENTRY(sla_trunk) entry;
729 AST_DECLARE_STRING_FIELDS(
730 AST_STRING_FIELD(name);
731 AST_STRING_FIELD(device);
732 AST_STRING_FIELD(autocontext);
734 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
735 /*! Number of stations that use this trunk */
736 unsigned int num_stations;
737 /*! Number of stations currently on a call with this trunk */
738 unsigned int active_stations;
739 /*! Number of stations that have this trunk on hold. */
740 unsigned int hold_stations;
741 struct ast_channel *chan;
742 unsigned int ring_timeout;
743 /*! If set to 1, no station will be able to join an active call with
745 unsigned int barge_disabled:1;
746 /*! This option uses the values in the sla_hold_access enum and sets the
747 * access control type for hold on this trunk. */
748 unsigned int hold_access:1;
749 /*! Whether this trunk is currently on hold, meaning that once a station
750 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
751 unsigned int on_hold:1;
752 /*! Use count for inside sla_trunk_exec */
753 unsigned int ref_count;
756 struct sla_trunk_ref {
757 AST_LIST_ENTRY(sla_trunk_ref) entry;
758 struct sla_trunk *trunk;
759 enum sla_trunk_state state;
760 struct ast_channel *chan;
761 /*! Ring timeout to use when this trunk is ringing on this specific
762 * station. This takes higher priority than a ring timeout set at
763 * the station level. */
764 unsigned int ring_timeout;
765 /*! Ring delay to use when this trunk is ringing on this specific
766 * station. This takes higher priority than a ring delay set at
767 * the station level. */
768 unsigned int ring_delay;
771 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
772 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
774 static const char sla_registrar[] = "SLA";
776 /*! \brief Event types that can be queued up for the SLA thread */
777 enum sla_event_type {
778 /*! A station has put the call on hold */
780 /*! The state of a dial has changed */
781 SLA_EVENT_DIAL_STATE,
782 /*! The state of a ringing trunk has changed */
783 SLA_EVENT_RINGING_TRUNK,
784 /*! A reload of configuration has been requested */
786 /*! Poke the SLA thread so it can check if it can perform a reload */
787 SLA_EVENT_CHECK_RELOAD,
791 enum sla_event_type type;
792 struct sla_station *station;
793 struct sla_trunk_ref *trunk_ref;
794 AST_LIST_ENTRY(sla_event) entry;
797 /*! \brief A station that failed to be dialed
798 * \note Only used by the SLA thread. */
799 struct sla_failed_station {
800 struct sla_station *station;
801 struct timeval last_try;
802 AST_LIST_ENTRY(sla_failed_station) entry;
805 /*! \brief A trunk that is ringing */
806 struct sla_ringing_trunk {
807 struct sla_trunk *trunk;
808 /*! The time that this trunk started ringing */
809 struct timeval ring_begin;
810 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
811 AST_LIST_ENTRY(sla_ringing_trunk) entry;
814 enum sla_station_hangup {
815 SLA_STATION_HANGUP_NORMAL,
816 SLA_STATION_HANGUP_TIMEOUT,
819 /*! \brief A station that is ringing */
820 struct sla_ringing_station {
821 struct sla_station *station;
822 /*! The time that this station started ringing */
823 struct timeval ring_begin;
824 AST_LIST_ENTRY(sla_ringing_station) entry;
828 * \brief A structure for data used by the sla thread
831 /*! The SLA thread ID */
835 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
836 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
837 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
838 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
840 /*! Attempt to handle CallerID, even though it is known not to work
841 * properly in some situations. */
842 unsigned int attempt_callerid:1;
843 /*! A reload has been requested */
844 unsigned int reload:1;
846 .thread = AST_PTHREADT_NULL,
849 /*! The number of audio buffers to be allocated on pseudo channels
850 * when in a conference */
851 static int audio_buffers;
853 /*! Map 'volume' levels from -5 through +5 into
854 * decibel (dB) settings for channel drivers
855 * Note: these are not a straight linear-to-dB
856 * conversion... the numbers have been modified
857 * to give the user a better level of adjustability
859 static char const gain_map[] = {
874 static int admin_exec(struct ast_channel *chan, void *data);
875 static void *recordthread(void *args);
877 static char *istalking(int x)
882 return "(unmonitored)";
884 return "(not talking)";
887 static int careful_write(int fd, unsigned char *data, int len, int block)
894 x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
895 res = ioctl(fd, DAHDI_IOMUX, &x);
899 res = write(fd, data, len);
901 if (errno != EAGAIN) {
902 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
914 static int set_talk_volume(struct ast_conf_user *user, int volume)
918 /* attempt to make the adjustment in the channel driver;
919 if successful, don't adjust in the frame reading routine
921 gain_adjust = gain_map[volume + 5];
923 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
926 static int set_listen_volume(struct ast_conf_user *user, int volume)
930 /* attempt to make the adjustment in the channel driver;
931 if successful, don't adjust in the frame reading routine
933 gain_adjust = gain_map[volume + 5];
935 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
938 static void tweak_volume(struct volume *vol, enum volume_action action)
942 switch (vol->desired) {
957 switch (vol->desired) {
973 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
975 tweak_volume(&user->talk, action);
976 /* attempt to make the adjustment in the channel driver;
977 if successful, don't adjust in the frame reading routine
979 if (!set_talk_volume(user, user->talk.desired))
980 user->talk.actual = 0;
982 user->talk.actual = user->talk.desired;
985 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
987 tweak_volume(&user->listen, action);
988 /* attempt to make the adjustment in the channel driver;
989 if successful, don't adjust in the frame reading routine
991 if (!set_listen_volume(user, user->listen.desired))
992 user->listen.actual = 0;
994 user->listen.actual = user->listen.desired;
997 static void reset_volumes(struct ast_conf_user *user)
999 signed char zero_volume = 0;
1001 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
1002 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
1005 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
1007 unsigned char *data;
1011 if (!ast_check_hangup(chan))
1012 res = ast_autoservice_start(chan);
1014 AST_LIST_LOCK(&confs);
1019 len = sizeof(enter);
1023 len = sizeof(leave);
1030 careful_write(conf->fd, data, len, 1);
1033 AST_LIST_UNLOCK(&confs);
1036 ast_autoservice_stop(chan);
1040 * \brief Find or create a conference
1042 * \param confno The conference name/number
1043 * \param pin The regular user pin
1044 * \param pinadmin The admin pin
1045 * \param make Make the conf if it doesn't exist
1046 * \param dynamic Mark the newly created conference as dynamic
1047 * \param refcount How many references to mark on the conference
1048 * \param chan The asterisk channel
1050 * \return A pointer to the conference struct, or NULL if it wasn't found and
1051 * make or dynamic were not set.
1053 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount, const struct ast_channel *chan)
1055 struct ast_conference *cnf;
1056 struct dahdi_confinfo dahdic = { 0, };
1059 AST_LIST_LOCK(&confs);
1061 AST_LIST_TRAVERSE(&confs, cnf, list) {
1062 if (!strcmp(confno, cnf->confno))
1066 if (cnf || (!make && !dynamic))
1069 /* Make a new one */
1070 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
1073 ast_mutex_init(&cnf->playlock);
1074 ast_mutex_init(&cnf->listenlock);
1075 cnf->recordthread = AST_PTHREADT_NULL;
1076 ast_mutex_init(&cnf->recordthreadlock);
1077 cnf->announcethread = AST_PTHREADT_NULL;
1078 ast_mutex_init(&cnf->announcethreadlock);
1079 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
1080 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
1081 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
1082 ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
1084 /* Setup a new dahdi conference */
1086 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1087 cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
1088 if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
1089 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
1097 cnf->dahdiconf = dahdic.confno;
1099 /* Setup a new channel for playback of audio files */
1100 cnf->chan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL);
1102 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
1103 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
1105 dahdic.confno = cnf->dahdiconf;
1106 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1107 if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &dahdic)) {
1108 ast_log(LOG_WARNING, "Error setting conference\n");
1110 ast_hangup(cnf->chan);
1120 /* Fill the conference struct */
1121 cnf->start = time(NULL);
1122 cnf->maxusers = 0x7fffffff;
1123 cnf->isdynamic = dynamic ? 1 : 0;
1124 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
1125 AST_LIST_INSERT_HEAD(&confs, cnf, list);
1127 /* Reserve conference number in map */
1128 if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1129 conf_map[confno_int] = 1;
1133 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
1135 AST_LIST_UNLOCK(&confs);
1140 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
1142 static char *cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
1144 int len = strlen(word);
1146 struct ast_conference *cnf = NULL;
1147 struct ast_conf_user *usr = NULL;
1148 char *confno = NULL;
1149 char usrno[50] = "";
1150 char *myline, *ret = NULL;
1152 if (pos == 1) { /* Command */
1153 return ast_cli_complete(word, cmds, state);
1154 } else if (pos == 2) { /* Conference Number */
1155 AST_LIST_LOCK(&confs);
1156 AST_LIST_TRAVERSE(&confs, cnf, list) {
1157 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
1162 ret = ast_strdup(ret); /* dup before releasing the lock */
1163 AST_LIST_UNLOCK(&confs);
1165 } else if (pos == 3) {
1166 /* User Number || Conf Command option*/
1167 if (strstr(line, "mute") || strstr(line, "kick")) {
1168 if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
1169 return ast_strdup("all");
1171 AST_LIST_LOCK(&confs);
1173 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
1174 myline = ast_strdupa(line);
1175 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
1176 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
1180 AST_LIST_TRAVERSE(&confs, cnf, list) {
1181 if (!strcmp(confno, cnf->confno))
1186 /* Search for the user */
1187 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
1188 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
1189 if (!strncasecmp(word, usrno, len) && ++which > state)
1193 AST_LIST_UNLOCK(&confs);
1194 return usr ? ast_strdup(usrno) : NULL;
1201 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1203 /* Process the command */
1204 struct ast_conf_user *user;
1205 struct ast_conference *cnf;
1207 int i = 0, total = 0;
1209 struct ast_str *cmdline = NULL;
1210 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
1211 #define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
1215 e->command = "meetme list [concise]";
1217 "Usage: meetme list [concise] <confno> \n"
1218 " List all or a specific conference.\n";
1221 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1224 /* Check for length so no buffer will overflow... */
1225 for (i = 0; i < a->argc; i++) {
1226 if (strlen(a->argv[i]) > 100)
1227 ast_cli(a->fd, "Invalid Arguments.\n");
1230 /* Max confno length */
1231 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1235 if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], "concise"))) {
1236 /* List all the conferences */
1237 int concise = (a->argc == 3 && !strcasecmp(a->argv[2], "concise"));
1239 AST_LIST_LOCK(&confs);
1240 if (AST_LIST_EMPTY(&confs)) {
1242 ast_cli(a->fd, "No active MeetMe conferences.\n");
1244 AST_LIST_UNLOCK(&confs);
1249 ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
1251 AST_LIST_TRAVERSE(&confs, cnf, list) {
1252 if (cnf->markedusers == 0) {
1253 ast_str_set(&cmdline, 0, "N/A ");
1255 ast_str_set(&cmdline, 0, "%4.4d", cnf->markedusers);
1257 hr = (now - cnf->start) / 3600;
1258 min = ((now - cnf->start) % 3600) / 60;
1259 sec = (now - cnf->start) % 60;
1261 ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users, cmdline->str, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
1263 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1272 total += cnf->users;
1274 AST_LIST_UNLOCK(&confs);
1276 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1280 } else if (strcmp(a->argv[1], "list") == 0) {
1281 int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
1282 /* List all the users in a conference */
1283 if (AST_LIST_EMPTY(&confs)) {
1285 ast_cli(a->fd, "No active MeetMe conferences.\n");
1290 /* Find the right conference */
1291 AST_LIST_LOCK(&confs);
1292 AST_LIST_TRAVERSE(&confs, cnf, list) {
1293 if (strcmp(cnf->confno, a->argv[2]) == 0) {
1299 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
1300 AST_LIST_UNLOCK(&confs);
1304 /* Show all the users */
1306 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
1307 hr = (now - user->jointime) / 3600;
1308 min = ((now - user->jointime) % 3600) / 60;
1309 sec = (now - user->jointime) % 60;
1311 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1313 S_OR(user->chan->cid.cid_num, "<unknown>"),
1314 S_OR(user->chan->cid.cid_name, "<no name>"),
1316 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
1317 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
1318 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1319 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1320 istalking(user->talking), hr, min, sec);
1322 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1324 S_OR(user->chan->cid.cid_num, ""),
1325 S_OR(user->chan->cid.cid_name, ""),
1327 user->userflags & CONFFLAG_ADMIN ? "1" : "",
1328 user->userflags & CONFFLAG_MONITOR ? "1" : "",
1329 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1330 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1331 user->talking, hr, min, sec);
1335 ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
1337 AST_LIST_UNLOCK(&confs);
1343 return CLI_SHOWUSAGE;
1346 ast_debug(1, "Cmdline: %s\n", cmdline->str);
1348 admin_exec(NULL, cmdline->str);
1355 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1357 /* Process the command */
1358 struct ast_str *cmdline = NULL;
1363 e->command = "meetme {lock|unlock|mute|unmute|kick}";
1365 "Usage: meetme (un)lock|(un)mute|kick <confno> <usernumber>\n"
1366 " Executes a command for the conference or on a conferee\n";
1369 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1373 ast_cli(a->fd, "Invalid Arguments.\n");
1374 /* Check for length so no buffer will overflow... */
1375 for (i = 0; i < a->argc; i++) {
1376 if (strlen(a->argv[i]) > 100)
1377 ast_cli(a->fd, "Invalid Arguments.\n");
1380 /* Max confno length */
1381 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1387 return CLI_SHOWUSAGE;
1390 ast_str_set(&cmdline, 0, "%s", a->argv[2]); /* Argv 2: conference number */
1391 if (strstr(a->argv[1], "lock")) {
1392 if (strcmp(a->argv[1], "lock") == 0) {
1394 ast_str_append(&cmdline, 0, ",L");
1397 ast_str_append(&cmdline, 0, ",l");
1399 } else if (strstr(a->argv[1], "mute")) {
1402 return CLI_SHOWUSAGE;
1404 if (strcmp(a->argv[1], "mute") == 0) {
1406 if (strcmp(a->argv[3], "all") == 0) {
1407 ast_str_append(&cmdline, 0, ",N");
1409 ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);
1413 if (strcmp(a->argv[3], "all") == 0) {
1414 ast_str_append(&cmdline, 0, ",n");
1416 ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
1419 } else if (strcmp(a->argv[1], "kick") == 0) {
1422 return CLI_SHOWUSAGE;
1424 if (strcmp(a->argv[3], "all") == 0) {
1426 ast_str_append(&cmdline, 0, ",K");
1428 /* Kick a single user */
1429 ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
1433 return CLI_SHOWUSAGE;
1436 ast_debug(1, "Cmdline: %s\n", cmdline->str);
1438 admin_exec(NULL, cmdline->str);
1444 static const char *sla_hold_str(unsigned int hold_access)
1446 const char *hold = "Unknown";
1448 switch (hold_access) {
1452 case SLA_HOLD_PRIVATE:
1461 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1463 const struct sla_trunk *trunk;
1467 e->command = "sla show trunks";
1469 "Usage: sla show trunks\n"
1470 " This will list all trunks defined in sla.conf\n";
1477 "=============================================================\n"
1478 "=== Configured SLA Trunks ===================================\n"
1479 "=============================================================\n"
1481 AST_RWLIST_RDLOCK(&sla_trunks);
1482 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1483 struct sla_station_ref *station_ref;
1484 char ring_timeout[16] = "(none)";
1485 if (trunk->ring_timeout)
1486 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1487 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1488 "=== Trunk Name: %s\n"
1489 "=== ==> Device: %s\n"
1490 "=== ==> AutoContext: %s\n"
1491 "=== ==> RingTimeout: %s\n"
1492 "=== ==> BargeAllowed: %s\n"
1493 "=== ==> HoldAccess: %s\n"
1494 "=== ==> Stations ...\n",
1495 trunk->name, trunk->device,
1496 S_OR(trunk->autocontext, "(none)"),
1498 trunk->barge_disabled ? "No" : "Yes",
1499 sla_hold_str(trunk->hold_access));
1500 AST_RWLIST_RDLOCK(&sla_stations);
1501 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1502 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
1503 AST_RWLIST_UNLOCK(&sla_stations);
1504 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
1506 AST_RWLIST_UNLOCK(&sla_trunks);
1507 ast_cli(a->fd, "=============================================================\n\n");
1512 static const char *trunkstate2str(enum sla_trunk_state state)
1514 #define S(e) case e: return # e;
1516 S(SLA_TRUNK_STATE_IDLE)
1517 S(SLA_TRUNK_STATE_RINGING)
1518 S(SLA_TRUNK_STATE_UP)
1519 S(SLA_TRUNK_STATE_ONHOLD)
1520 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1522 return "Uknown State";
1526 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1528 const struct sla_station *station;
1532 e->command = "sla show stations";
1534 "Usage: sla show stations\n"
1535 " This will list all stations defined in sla.conf\n";
1542 "=============================================================\n"
1543 "=== Configured SLA Stations =================================\n"
1544 "=============================================================\n"
1546 AST_RWLIST_RDLOCK(&sla_stations);
1547 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1548 struct sla_trunk_ref *trunk_ref;
1549 char ring_timeout[16] = "(none)";
1550 char ring_delay[16] = "(none)";
1551 if (station->ring_timeout) {
1552 snprintf(ring_timeout, sizeof(ring_timeout),
1553 "%u", station->ring_timeout);
1555 if (station->ring_delay) {
1556 snprintf(ring_delay, sizeof(ring_delay),
1557 "%u", station->ring_delay);
1559 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1560 "=== Station Name: %s\n"
1561 "=== ==> Device: %s\n"
1562 "=== ==> AutoContext: %s\n"
1563 "=== ==> RingTimeout: %s\n"
1564 "=== ==> RingDelay: %s\n"
1565 "=== ==> HoldAccess: %s\n"
1566 "=== ==> Trunks ...\n",
1567 station->name, station->device,
1568 S_OR(station->autocontext, "(none)"),
1569 ring_timeout, ring_delay,
1570 sla_hold_str(station->hold_access));
1571 AST_RWLIST_RDLOCK(&sla_trunks);
1572 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1573 if (trunk_ref->ring_timeout) {
1574 snprintf(ring_timeout, sizeof(ring_timeout),
1575 "%u", trunk_ref->ring_timeout);
1577 strcpy(ring_timeout, "(none)");
1578 if (trunk_ref->ring_delay) {
1579 snprintf(ring_delay, sizeof(ring_delay),
1580 "%u", trunk_ref->ring_delay);
1582 strcpy(ring_delay, "(none)");
1583 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
1584 "=== ==> State: %s\n"
1585 "=== ==> RingTimeout: %s\n"
1586 "=== ==> RingDelay: %s\n",
1587 trunk_ref->trunk->name,
1588 trunkstate2str(trunk_ref->state),
1589 ring_timeout, ring_delay);
1591 AST_RWLIST_UNLOCK(&sla_trunks);
1592 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1595 AST_RWLIST_UNLOCK(&sla_stations);
1596 ast_cli(a->fd, "============================================================\n"
1602 static struct ast_cli_entry cli_meetme[] = {
1603 AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
1604 AST_CLI_DEFINE(meetme_show_cmd, "List all or one conference"),
1605 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
1606 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
1609 static void conf_flush(int fd, struct ast_channel *chan)
1613 /* read any frames that may be waiting on the channel
1617 struct ast_frame *f;
1619 /* when no frames are available, this will wait
1620 for 1 millisecond maximum
1622 while (ast_waitfor(chan, 1)) {
1626 else /* channel was hung up or something else happened */
1631 /* flush any data sitting in the pseudo channel */
1632 x = DAHDI_FLUSH_ALL;
1633 if (ioctl(fd, DAHDI_FLUSH, &x))
1634 ast_log(LOG_WARNING, "Error flushing channel\n");
1638 /* Remove the conference from the list and free it.
1639 We assume that this was called while holding conflock. */
1640 static int conf_free(struct ast_conference *conf)
1643 struct announce_listitem *item;
1645 AST_LIST_REMOVE(&confs, conf, list);
1646 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1648 if (conf->recording == MEETME_RECORD_ACTIVE) {
1649 conf->recording = MEETME_RECORD_TERMINATE;
1650 AST_LIST_UNLOCK(&confs);
1653 AST_LIST_LOCK(&confs);
1654 if (conf->recording == MEETME_RECORD_OFF)
1656 AST_LIST_UNLOCK(&confs);
1660 for (x = 0; x < AST_FRAME_BITS; x++) {
1661 if (conf->transframe[x])
1662 ast_frfree(conf->transframe[x]);
1663 if (conf->transpath[x])
1664 ast_translator_free_path(conf->transpath[x]);
1666 if (conf->announcethread != AST_PTHREADT_NULL) {
1667 ast_mutex_lock(&conf->announcelistlock);
1668 conf->announcethread_stop = 1;
1669 ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
1670 ast_cond_signal(&conf->announcelist_addition);
1671 ast_mutex_unlock(&conf->announcelistlock);
1672 pthread_join(conf->announcethread, NULL);
1674 while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
1675 ast_filedelete(item->namerecloc, NULL);
1678 ast_mutex_destroy(&conf->announcelistlock);
1680 if (conf->origframe)
1681 ast_frfree(conf->origframe);
1683 ast_hangup(conf->lchan);
1685 ast_hangup(conf->chan);
1688 if (conf->recordingfilename) {
1689 ast_free(conf->recordingfilename);
1691 if (conf->recordingformat) {
1692 ast_free(conf->recordingformat);
1694 ast_mutex_destroy(&conf->playlock);
1695 ast_mutex_destroy(&conf->listenlock);
1696 ast_mutex_destroy(&conf->recordthreadlock);
1697 ast_mutex_destroy(&conf->announcethreadlock);
1703 static void conf_queue_dtmf(const struct ast_conference *conf,
1704 const struct ast_conf_user *sender, struct ast_frame *f)
1706 struct ast_conf_user *user;
1708 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1711 if (ast_write(user->chan, f) < 0)
1712 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1716 static void sla_queue_event_full(enum sla_event_type type,
1717 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1719 struct sla_event *event;
1721 if (sla.thread == AST_PTHREADT_NULL) {
1725 if (!(event = ast_calloc(1, sizeof(*event))))
1729 event->trunk_ref = trunk_ref;
1730 event->station = station;
1733 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1737 ast_mutex_lock(&sla.lock);
1738 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1739 ast_cond_signal(&sla.cond);
1740 ast_mutex_unlock(&sla.lock);
1743 static void sla_queue_event_nolock(enum sla_event_type type)
1745 sla_queue_event_full(type, NULL, NULL, 0);
1748 static void sla_queue_event(enum sla_event_type type)
1750 sla_queue_event_full(type, NULL, NULL, 1);
1753 /*! \brief Queue a SLA event from the conference */
1754 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1755 struct ast_conference *conf)
1757 struct sla_station *station;
1758 struct sla_trunk_ref *trunk_ref = NULL;
1761 trunk_name = ast_strdupa(conf->confno);
1762 strsep(&trunk_name, "_");
1763 if (ast_strlen_zero(trunk_name)) {
1764 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1768 AST_RWLIST_RDLOCK(&sla_stations);
1769 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1770 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1771 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1777 AST_RWLIST_UNLOCK(&sla_stations);
1780 ast_debug(1, "Trunk not found for event!\n");
1784 sla_queue_event_full(type, trunk_ref, station, 1);
1787 /* Decrement reference counts, as incremented by find_conf() */
1788 static int dispose_conf(struct ast_conference *conf)
1793 AST_LIST_LOCK(&confs);
1794 if (ast_atomic_dec_and_test(&conf->refcount)) {
1795 /* Take the conference room number out of an inuse state */
1796 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
1797 conf_map[confno_int] = 0;
1802 AST_LIST_UNLOCK(&confs);
1807 static int rt_extend_conf(char *confno)
1809 char currenttime[32];
1813 struct ast_variable *var;
1822 ast_localtime(&now, &tm, NULL);
1823 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1825 var = ast_load_realtime("meetme", "confno",
1826 confno, "startTime<= ", currenttime,
1827 "endtime>= ", currenttime, NULL);
1829 /* Identify the specific RealTime conference */
1831 if (!strcasecmp(var->name, "bookid")) {
1832 ast_copy_string(bookid, var->value, sizeof(bookid));
1834 if (!strcasecmp(var->name, "endtime")) {
1835 ast_copy_string(endtime, var->value, sizeof(endtime));
1840 ast_variables_destroy(var);
1842 ast_strptime(endtime, DATE_FORMAT, &tm);
1843 now = ast_mktime(&tm, NULL);
1845 now.tv_sec += extendby;
1847 ast_localtime(&now, &tm, NULL);
1848 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1849 strcat(currenttime, "0"); /* Seconds needs to be 00 */
1851 var = ast_load_realtime("meetme", "confno",
1852 confno, "startTime<= ", currenttime,
1853 "endtime>= ", currenttime, NULL);
1855 /* If there is no conflict with extending the conference, update the DB */
1857 ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
1858 ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
1863 ast_variables_destroy(var);
1867 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
1871 ast_channel_lock(chan);
1872 original_moh = ast_strdupa(chan->musicclass);
1873 ast_string_field_set(chan, musicclass, musicclass);
1874 ast_channel_unlock(chan);
1876 ast_moh_start(chan, original_moh, NULL);
1878 ast_channel_lock(chan);
1879 ast_string_field_set(chan, musicclass, original_moh);
1880 ast_channel_unlock(chan);
1883 static const char *get_announce_filename(enum announcetypes type)
1887 return "conf-hasleft";
1890 return "conf-hasjoin";
1897 static void *announce_thread(void *data)
1899 struct announce_listitem *current;
1900 struct ast_conference *conf = data;
1902 char filename[PATH_MAX] = "";
1903 AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
1904 AST_LIST_HEAD_INIT_NOLOCK(&local_list);
1906 while (!conf->announcethread_stop) {
1907 ast_mutex_lock(&conf->announcelistlock);
1908 if (conf->announcethread_stop) {
1909 ast_mutex_unlock(&conf->announcelistlock);
1912 if (AST_LIST_EMPTY(&conf->announcelist))
1913 ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
1915 AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
1916 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
1918 ast_mutex_unlock(&conf->announcelistlock);
1919 if (conf->announcethread_stop) {
1923 for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
1924 ast_log(LOG_DEBUG, "About to play %s\n", current->namerecloc);
1925 if (!ast_fileexists(current->namerecloc, NULL, NULL))
1927 if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
1928 if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
1929 res = ast_waitstream(current->confchan, "");
1931 ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
1932 if (!ast_streamfile(current->confchan, filename, current->language))
1933 ast_waitstream(current->confchan, "");
1936 if (current->announcetype == CONF_HASLEFT) {
1937 ast_filedelete(current->namerecloc, NULL);
1942 /* thread marked to stop, clean up */
1943 while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
1944 ast_filedelete(current->namerecloc, NULL);
1945 ao2_ref(current, -1);
1950 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1952 struct ast_conf_user *user = NULL;
1953 struct ast_conf_user *usr = NULL;
1955 struct dahdi_confinfo dahdic, dahdic_empty;
1956 struct ast_frame *f;
1957 struct ast_channel *c;
1958 struct ast_frame fr;
1966 int musiconhold = 0;
1969 int currentmarked = 0;
1972 int menu_active = 0;
1973 int talkreq_manager = 0;
1974 int using_pseudo = 0;
1979 int announcement_played = 0;
1981 struct ast_dsp *dsp = NULL;
1982 struct ast_app *agi_app;
1984 const char *agifiledefault = "conf-background.agi", *tmpvar;
1985 char meetmesecs[30] = "";
1986 char exitcontext[AST_MAX_CONTEXT] = "";
1987 char recordingtmp[AST_MAX_EXTENSION] = "";
1988 char members[10] = "";
1989 int dtmf, opt_waitmarked_timeout = 0;
1991 struct dahdi_bufferinfo bi;
1992 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1993 char *buf = __buf + AST_FRIENDLY_OFFSET;
1994 char *exitkeys = NULL;
1995 unsigned int calldurationlimit = 0;
1997 long play_warning = 0;
1998 long warning_freq = 0;
1999 const char *warning_sound = NULL;
2000 const char *end_sound = NULL;
2002 long time_left_ms = 0;
2003 struct timeval nexteventts = { 0, };
2005 int setusercount = 0;
2007 if (!(user = ast_calloc(1, sizeof(*user))))
2010 /* Possible timeout waiting for marked user */
2011 if ((confflags & CONFFLAG_WAITMARKED) &&
2012 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
2013 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
2014 (opt_waitmarked_timeout > 0)) {
2015 timeout = time(NULL) + opt_waitmarked_timeout;
2018 if ((confflags & CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
2019 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
2020 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
2023 if ((confflags & CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
2024 char *limit_str, *warning_str, *warnfreq_str;
2027 parse = optargs[OPT_ARG_DURATION_LIMIT];
2028 limit_str = strsep(&parse, ":");
2029 warning_str = strsep(&parse, ":");
2030 warnfreq_str = parse;
2032 timelimit = atol(limit_str);
2034 play_warning = atol(warning_str);
2036 warning_freq = atol(warnfreq_str);
2039 timelimit = play_warning = warning_freq = 0;
2040 warning_sound = NULL;
2041 } else if (play_warning > timelimit) {
2042 if (!warning_freq) {
2045 while (play_warning > timelimit)
2046 play_warning -= warning_freq;
2047 if (play_warning < 1)
2048 play_warning = warning_freq = 0;
2052 ast_channel_lock(chan);
2053 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
2054 var = ast_strdupa(var);
2056 ast_channel_unlock(chan);
2058 warning_sound = var ? var : "timeleft";
2060 ast_channel_lock(chan);
2061 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
2062 var = ast_strdupa(var);
2064 ast_channel_unlock(chan);
2066 end_sound = var ? var : NULL;
2068 /* undo effect of S(x) in case they are both used */
2069 calldurationlimit = 0;
2070 /* more efficient do it like S(x) does since no advanced opts */
2071 if (!play_warning && !end_sound && timelimit) {
2072 calldurationlimit = timelimit / 1000;
2073 timelimit = play_warning = warning_freq = 0;
2075 ast_debug(2, "Limit Data for this call:\n");
2076 ast_debug(2, "- timelimit = %ld\n", timelimit);
2077 ast_debug(2, "- play_warning = %ld\n", play_warning);
2078 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
2079 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
2080 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
2085 if ((confflags & CONFFLAG_KEYEXIT)) {
2086 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
2087 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
2089 exitkeys = ast_strdupa("#"); /* Default */
2092 if (confflags & CONFFLAG_RECORDCONF) {
2093 if (!conf->recordingfilename) {
2095 ast_channel_lock(chan);
2096 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
2097 conf->recordingfilename = ast_strdup(var);
2099 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
2100 conf->recordingformat = ast_strdup(var);
2102 ast_channel_unlock(chan);
2103 if (!conf->recordingfilename) {
2104 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
2105 conf->recordingfilename = ast_strdup(recordingtmp);
2107 if (!conf->recordingformat) {
2108 conf->recordingformat = ast_strdup("wav");
2110 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
2111 conf->confno, conf->recordingfilename, conf->recordingformat);
2115 ast_mutex_lock(&conf->recordthreadlock);
2116 if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
2117 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
2118 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
2120 dahdic.confno = conf->dahdiconf;
2121 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
2122 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
2123 ast_log(LOG_WARNING, "Error starting listen channel\n");
2124 ast_hangup(conf->lchan);
2127 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
2130 ast_mutex_unlock(&conf->recordthreadlock);
2132 ast_mutex_lock(&conf->announcethreadlock);
2133 if ((conf->announcethread == AST_PTHREADT_NULL) && !(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2134 ast_mutex_init(&conf->announcelistlock);
2135 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2136 ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
2138 ast_mutex_unlock(&conf->announcethreadlock);
2140 time(&user->jointime);
2142 user->timelimit = timelimit;
2143 user->play_warning = play_warning;
2144 user->warning_freq = warning_freq;
2145 user->warning_sound = warning_sound;
2146 user->end_sound = end_sound;
2148 if (calldurationlimit > 0) {
2149 time(&user->kicktime);
2150 user->kicktime = user->kicktime + calldurationlimit;
2153 if (ast_tvzero(user->start_time))
2154 user->start_time = ast_tvnow();
2155 time_left_ms = user->timelimit;
2157 if (user->timelimit) {
2158 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2159 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
2162 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
2163 /* Sorry, but this conference is locked! */
2164 if (!ast_streamfile(chan, "conf-locked", chan->language))
2165 ast_waitstream(chan, "");
2169 ast_mutex_lock(&conf->playlock);
2171 if (AST_LIST_EMPTY(&conf->userlist))
2174 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
2176 if (rt_schedule && conf->maxusers)
2177 if (user->user_no > conf->maxusers) {
2178 /* Sorry, but this confernce has reached the participant limit! */
2179 if (!ast_streamfile(chan, "conf-full", chan->language))
2180 ast_waitstream(chan, "");
2184 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
2187 user->userflags = confflags;
2188 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
2191 ast_mutex_unlock(&conf->playlock);
2193 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2194 char destdir[PATH_MAX];
2196 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
2198 if (ast_mkdir(destdir, 0777) != 0) {
2199 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
2203 snprintf(user->namerecloc, sizeof(user->namerecloc),
2204 "%s/meetme-username-%s-%d", destdir,
2205 conf->confno, user->user_no);
2206 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
2207 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
2209 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
2214 ast_mutex_lock(&conf->playlock);
2216 if (confflags & CONFFLAG_MARKEDUSER)
2217 conf->markedusers++;
2219 if (rt_log_members) {
2221 snprintf(members, sizeof(members), "%d", conf->users);
2222 ast_realtime_require_field("meetme",
2223 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
2224 "members", RQ_UINTEGER1, strlen(members),
2226 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2230 /* This device changed state now - if this is the first user */
2231 if (conf->users == 1)
2232 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
2234 ast_mutex_unlock(&conf->playlock);
2236 /* return the unique ID of the conference */
2237 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
2239 if (confflags & CONFFLAG_EXIT_CONTEXT) {
2240 ast_channel_lock(chan);
2241 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
2242 ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
2243 } else if (!ast_strlen_zero(chan->macrocontext)) {
2244 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
2246 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
2248 ast_channel_unlock(chan);
2251 if (!(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
2252 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
2253 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
2254 ast_waitstream(chan, "");
2255 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
2256 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
2257 ast_waitstream(chan, "");
2260 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
2261 int keepplaying = 1;
2263 if (conf->users == 2) {
2264 if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
2265 res = ast_waitstream(chan, AST_DIGIT_ANY);
2266 ast_stopstream(chan);
2273 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
2274 res = ast_waitstream(chan, AST_DIGIT_ANY);
2275 ast_stopstream(chan);
2282 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2288 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
2289 res = ast_waitstream(chan, AST_DIGIT_ANY);
2290 ast_stopstream(chan);
2299 ast_indicate(chan, -1);
2301 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
2302 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
2306 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
2307 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
2311 retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
2312 user->dahdichannel = !retrydahdi;
2315 origfd = chan->fds[0];
2317 fd = open("/dev/dahdi/pseudo", O_RDWR);
2319 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
2323 /* Make non-blocking */
2324 flags = fcntl(fd, F_GETFL);
2326 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
2330 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
2331 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
2335 /* Setup buffering information */
2336 memset(&bi, 0, sizeof(bi));
2337 bi.bufsize = CONF_SIZE / 2;
2338 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
2339 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
2340 bi.numbufs = audio_buffers;
2341 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
2342 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
2347 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
2348 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
2354 /* XXX Make sure we're not running on a pseudo channel XXX */
2358 memset(&dahdic, 0, sizeof(dahdic));
2359 memset(&dahdic_empty, 0, sizeof(dahdic_empty));
2360 /* Check to see if we're in a conference... */
2362 if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
2363 ast_log(LOG_WARNING, "Error getting conference\n");
2367 if (dahdic.confmode) {
2368 /* Whoa, already in a conference... Retry... */
2370 ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
2375 memset(&dahdic, 0, sizeof(dahdic));
2376 /* Add us to the conference */
2378 dahdic.confno = conf->dahdiconf;
2380 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
2381 struct announce_listitem *item;
2382 if (!(item = ao2_alloc(sizeof(*item), NULL)))
2384 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
2385 ast_copy_string(item->language, chan->language, sizeof(item->language));
2386 item->confchan = conf->chan;
2387 item->confusers = conf->users;
2388 item->announcetype = CONF_HASJOIN;
2389 ast_mutex_lock(&conf->announcelistlock);
2390 ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
2391 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
2392 ast_cond_signal(&conf->announcelist_addition);
2393 ast_mutex_unlock(&conf->announcelistlock);
2395 while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
2401 if (confflags & CONFFLAG_WAITMARKED && !conf->markedusers)
2402 dahdic.confmode = DAHDI_CONF_CONF;
2403 else if (confflags & CONFFLAG_MONITOR)
2404 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2405 else if (confflags & CONFFLAG_TALKER)
2406 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2408 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2410 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2411 ast_log(LOG_WARNING, "Error setting conference\n");
2415 ast_debug(1, "Placed channel %s in DAHDI conf %d\n", chan->name, conf->dahdiconf);
2418 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
2423 "CallerIDnum: %s\r\n"
2424 "CallerIDname: %s\r\n",
2425 chan->name, chan->uniqueid, conf->confno,
2427 S_OR(user->chan->cid.cid_num, "<unknown>"),
2428 S_OR(user->chan->cid.cid_name, "<unknown>")
2433 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
2435 if (!(confflags & CONFFLAG_QUIET))
2436 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
2437 conf_play(chan, conf, ENTER);
2440 conf_flush(fd, chan);
2442 if (confflags & CONFFLAG_AGI) {
2443 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
2444 or use default filename of conf-background.agi */
2446 ast_channel_lock(chan);
2447 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
2448 agifile = ast_strdupa(tmpvar);
2450 agifile = ast_strdupa(agifiledefault);
2452 ast_channel_unlock(chan);
2454 if (user->dahdichannel) {
2455 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
2457 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2459 /* Find a pointer to the agi app and execute the script */
2460 agi_app = pbx_findapp("agi");
2462 ret = pbx_exec(chan, agi_app, agifile);
2464 ast_log(LOG_WARNING, "Could not find application (agi)\n");
2467 if (user->dahdichannel) {
2468 /* Remove CONFMUTE mode on DAHDI channel */
2470 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2473 if (user->dahdichannel && (confflags & CONFFLAG_STARMENU)) {
2474 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
2476 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2478 if (!(confflags & CONFFLAG_MONITOR) && !(dsp = ast_dsp_new())) {
2479 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
2483 int menu_was_active = 0;
2489 if (rt_schedule && conf->endtime) {
2490 char currenttime[32];
2491 long localendtime = 0;
2494 struct ast_variable *var, *origvar;
2497 if (now.tv_sec % 60 == 0) {
2499 ast_localtime(&now, &tm, NULL);
2500 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2501 var = origvar = ast_load_realtime("meetme", "confno",
2502 conf->confno, "starttime <=", currenttime,
2503 "endtime >=", currenttime, NULL);
2505 for ( ; var; var = var->next) {
2506 if (!strcasecmp(var->name, "endtime")) {
2507 struct ast_tm endtime_tm;
2508 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
2509 tmp = ast_mktime(&endtime_tm, NULL);
2510 localendtime = tmp.tv_sec;
2513 ast_variables_destroy(origvar);
2515 /* A conference can be extended from the
2516 Admin/User menu or by an external source */
2517 if (localendtime > conf->endtime){
2518 conf->endtime = localendtime;
2522 if (conf->endtime && (now.tv_sec >= conf->endtime)) {
2523 ast_verbose("Quitting time...\n");
2527 if (!announcement_played && conf->endalert) {
2528 if (now.tv_sec + conf->endalert >= conf->endtime) {
2529 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
2530 ast_waitstream(chan, "");
2531 ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", chan->language);
2532 if (!ast_streamfile(chan, "minutes", chan->language))
2533 ast_waitstream(chan, "");
2534 announcement_played = 1;
2539 announcement_played = 0;
2549 if (user->kicktime && (user->kicktime <= now.tv_sec)) {
2554 if (user->timelimit) {
2555 int minutes = 0, seconds = 0, remain = 0;
2557 to = ast_tvdiff_ms(nexteventts, now);
2561 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
2562 if (time_left_ms < to) {
2566 if (time_left_ms <= 0) {
2567 if (user->end_sound) {
2568 res = ast_streamfile(chan, user->end_sound, chan->language);
2569 res = ast_waitstream(chan, "");
2575 if (time_left_ms >= 5000) {
2577 remain = (time_left_ms + 500) / 1000;
2578 if (remain / 60 >= 1) {
2579 minutes = remain / 60;
2580 seconds = remain % 60;
2585 /* force the time left to round up if appropriate */
2586 if (user->warning_sound && user->play_warning) {
2587 if (!strcmp(user->warning_sound, "timeleft")) {
2589 res = ast_streamfile(chan, "vm-youhave", chan->language);
2590 res = ast_waitstream(chan, "");
2592 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
2593 res = ast_streamfile(chan, "queue-minutes", chan->language);
2594 res = ast_waitstream(chan, "");
2597 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
2598 res = ast_streamfile(chan, "queue-seconds", chan->language);
2599 res = ast_waitstream(chan, "");
2602 res = ast_streamfile(chan, user->warning_sound, chan->language);
2603 res = ast_waitstream(chan, "");
2607 if (user->warning_freq) {
2608 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
2610 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2616 if (timeout && now.tv_sec >= timeout) {
2620 /* if we have just exited from the menu, and the user had a channel-driver
2621 volume adjustment, restore it
2623 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual) {
2624 set_talk_volume(user, user->listen.desired);
2627 menu_was_active = menu_active;
2629 currentmarked = conf->markedusers;
2630 if (!(confflags & CONFFLAG_QUIET) &&
2631 (confflags & CONFFLAG_MARKEDUSER) &&
2632 (confflags & CONFFLAG_WAITMARKED) &&
2634 if (currentmarked == 1 && conf->users > 1) {
2635 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2636 if (conf->users - 1 == 1) {
2637 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language)) {
2638 ast_waitstream(chan, "");
2641 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language)) {
2642 ast_waitstream(chan, "");
2646 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER)) {
2647 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
2648 ast_waitstream(chan, "");
2653 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
2655 /* Update the struct with the actual confflags */
2656 user->userflags = confflags;
2658 if (confflags & CONFFLAG_WAITMARKED) {
2659 if (currentmarked == 0) {
2660 if (lastmarked != 0) {
2661 if (!(confflags & CONFFLAG_QUIET)) {
2662 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language)) {
2663 ast_waitstream(chan, "");
2666 if (confflags & CONFFLAG_MARKEDEXIT) {
2667 if (confflags & CONFFLAG_KICK_CONTINUE) {
2672 dahdic.confmode = DAHDI_CONF_CONF;
2673 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2674 ast_log(LOG_WARNING, "Error setting conference\n");
2680 if (!musiconhold && (confflags & CONFFLAG_MOH)) {
2681 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2684 } else if (currentmarked >= 1 && lastmarked == 0) {
2685 /* Marked user entered, so cancel timeout */
2687 if (confflags & CONFFLAG_MONITOR) {
2688 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2689 } else if (confflags & CONFFLAG_TALKER) {
2690 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2692 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2694 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2695 ast_log(LOG_WARNING, "Error setting conference\n");
2699 if (musiconhold && (confflags & CONFFLAG_MOH)) {
2703 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
2704 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language)) {
2705 ast_waitstream(chan, "");
2707 conf_play(chan, conf, ENTER);
2712 /* trying to add moh for single person conf */
2713 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
2714 if (conf->users == 1) {
2716 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2727 /* Leave if the last marked user left */
2728 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
2729 if (confflags & CONFFLAG_KICK_CONTINUE) {
2737 /* Check if my modes have changed */
2739 /* If I should be muted but am still talker, mute me */
2740 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
2741 dahdic.confmode ^= DAHDI_CONF_TALKER;
2742 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2743 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2748 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2754 chan->name, chan->uniqueid, conf->confno, user->user_no);
2757 /* If I should be un-muted but am not talker, un-mute me */
2758 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
2759 dahdic.confmode |= DAHDI_CONF_TALKER;
2760 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2761 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2766 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2772 chan->name, chan->uniqueid, conf->confno, user->user_no);
2775 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2776 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
2777 talkreq_manager = 1;
2779 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2785 chan->name, chan->uniqueid, conf->confno, user->user_no);
2789 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2790 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
2791 talkreq_manager = 0;
2792 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2798 chan->name, chan->uniqueid, conf->confno, user->user_no);
2801 /* If I have been kicked, exit the conference */
2802 if (user->adminflags & ADMINFLAG_KICKME) {
2803 /* You have been kicked. */
2804 if (!(confflags & CONFFLAG_QUIET) &&
2805 !ast_streamfile(chan, "conf-kicked", chan->language)) {
2806 ast_waitstream(chan, "");
2812 /* Perform an extra hangup check just in case */
2813 if (ast_check_hangup(chan)) {
2818 char dtmfstr[2] = "";
2820 if (c->fds[0] != origfd || (user->dahdichannel && (c->audiohooks || c->monitor))) {
2822 /* Kill old pseudo */
2826 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
2827 retrydahdi = (strcasecmp(c->tech->type, "DAHDI") || (c->audiohooks || c->monitor) ? 1 : 0);
2828 user->dahdichannel = !retrydahdi;
2831 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2832 f = ast_read_noaudio(c);
2839 if (f->frametype == AST_FRAME_DTMF) {
2840 dtmfstr[0] = f->subclass;
2844 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
2845 if (user->talk.actual) {
2846 ast_frame_adjust_volume(f, user->talk.actual);
2849 if (!(confflags & CONFFLAG_MONITOR)) {
2852 if (user->talking == -1) {
2856 res = ast_dsp_silence(dsp, f, &totalsilence);
2857 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
2859 if (confflags & CONFFLAG_MONITORTALKER)
2860 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2866 chan->name, chan->uniqueid, conf->confno, user->user_no);
2868 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
2870 if (confflags & CONFFLAG_MONITORTALKER) {
2871 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2877 chan->name, chan->uniqueid, conf->confno, user->user_no);
2882 /* Absolutely do _not_ use careful_write here...
2883 it is important that we read data from the channel
2884 as fast as it arrives, and feed it into the conference.
2885 The buffering in the pseudo channel will take care of any
2886 timing differences, unless they are so drastic as to lose
2887 audio frames (in which case carefully writing would only
2888 have delayed the audio even further).
2890 /* As it turns out, we do want to use careful write. We just
2891 don't want to block, but we do want to at least *try*
2892 to write out all the samples.
2894 if (user->talking) {
2895 careful_write(fd, f->data.ptr, f->datalen, 0);
2898 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
2899 if (confflags & CONFFLAG_PASS_DTMF) {
2900 conf_queue_dtmf(conf, user, f);
2902 if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
2903 ast_log(LOG_WARNING, "Error setting conference\n");
2909 /* if we are entering the menu, and the user has a channel-driver
2910 volume adjustment, clear it
2912 if (!menu_active && user->talk.desired && !user->talk.actual) {
2913 set_talk_volume(user, 0);
2919 if ((confflags & CONFFLAG_ADMIN)) {
2923 /* Record this sound! */
2924 if (!ast_streamfile(chan, "conf-adminmenu-162", chan->language)) {
2925 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2926 ast_stopstream(chan);
2935 case '1': /* Un/Mute */
2938 /* for admin, change both admin and use flags */
2939 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
2940 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2942 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2945 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2946 if (!ast_streamfile(chan, "conf-muted", chan->language)) {
2947 ast_waitstream(chan, "");
2950 if (!ast_streamfile(chan, "conf-unmuted", chan->language)) {
2951 ast_waitstream(chan, "");
2955 case '2': /* Un/Lock the Conference */
2959 if (!ast_streamfile(chan, "conf-unlockednow", chan->language)) {
2960 ast_waitstream(chan, "");
2964 if (!ast_streamfile(chan, "conf-lockednow", chan->language)) {
2965 ast_waitstream(chan, "");
2969 case '3': /* Eject last user */
2971 usr = AST_LIST_LAST(&conf->userlist);
2972 if ((usr->chan->name == chan->name) || (usr->userflags & CONFFLAG_ADMIN)) {
2973 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
2974 ast_waitstream(chan, "");
2977 usr->adminflags |= ADMINFLAG_KICKME;
2979 ast_stopstream(chan);
2982 tweak_listen_volume(user, VOL_DOWN);
2985 /* Extend RT conference */
2987 if (!rt_extend_conf(conf->confno)) {
2988 if (!ast_streamfile(chan, "conf-extended", chan->language)) {
2989 ast_waitstream(chan, "");
2992 if (!ast_streamfile(chan, "conf-nonextended", chan->language)) {
2993 ast_waitstream(chan, "");
2996 ast_stopstream(chan);
3001 tweak_listen_volume(user, VOL_UP);
3004 tweak_talk_volume(user, VOL_DOWN);
3010 tweak_talk_volume(user, VOL_UP);
3014 /* Play an error message! */
3015 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
3016 ast_waitstream(chan, "");
3025 if (!ast_streamfile(chan, "con