Asterisk - The Open Source Telephony Project  GIT-master-8beac82
app_meetme.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2007, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * SLA Implementation by:
9  * Russell Bryant <russell@digium.com>
10  *
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.
16  *
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.
20  */
21 
22 /*! \file
23  *
24  * \brief Meet me conference bridge and Shared Line Appearances
25  *
26  * \author Mark Spencer <markster@digium.com>
27  * \author (SLA) Russell Bryant <russell@digium.com>
28  *
29  * \ingroup applications
30  */
31 
32 /*! \li \ref app_meetme.c uses configuration file \ref meetme.conf
33  * \addtogroup configuration_file Configuration Files
34  */
35 
36 /*!
37  * \page meetme.conf meetme.conf
38  * \verbinclude meetme.conf.sample
39  */
40 
41 /*** MODULEINFO
42  <depend>dahdi</depend>
43  <defaultenabled>no</defaultenabled>
44  <support_level>deprecated</support_level>
45  <replacement>app_confbridge</replacement>
46  <deprecated_in>19</deprecated_in>
47  <removed_in>21</removed_in>
48  ***/
49 
50 #include "asterisk.h"
51 
52 #include <dahdi/user.h>
53 
54 #include "asterisk/lock.h"
55 #include "asterisk/file.h"
56 #include "asterisk/channel.h"
57 #include "asterisk/pbx.h"
58 #include "asterisk/module.h"
59 #include "asterisk/config.h"
60 #include "asterisk/app.h"
61 #include "asterisk/dsp.h"
62 #include "asterisk/musiconhold.h"
63 #include "asterisk/manager.h"
64 #include "asterisk/cli.h"
65 #include "asterisk/say.h"
66 #include "asterisk/utils.h"
67 #include "asterisk/translate.h"
68 #include "asterisk/ulaw.h"
69 #include "asterisk/astobj2.h"
70 #include "asterisk/devicestate.h"
71 #include "asterisk/dial.h"
72 #include "asterisk/causes.h"
73 #include "asterisk/paths.h"
74 #include "asterisk/test.h"
75 #include "asterisk/stasis.h"
78 #include "asterisk/json.h"
80 
81 #include "enter.h"
82 #include "leave.h"
83 
84 /*** DOCUMENTATION
85  <application name="MeetMe" language="en_US">
86  <synopsis>
87  MeetMe conference bridge.
88  </synopsis>
89  <syntax>
90  <parameter name="confno">
91  <para>The conference number</para>
92  </parameter>
93  <parameter name="options">
94  <optionlist>
95  <option name="a">
96  <para>Set admin mode.</para>
97  </option>
98  <option name="A">
99  <para>Set marked mode.</para>
100  </option>
101  <option name="b">
102  <para>Run AGI script specified in <variable>MEETME_AGI_BACKGROUND</variable>
103  Default: <literal>conf-background.agi</literal>.</para>
104  <note><para>This does not work with non-DAHDI channels in the same
105  conference).</para></note>
106  </option>
107  <option name="c">
108  <para>Announce user(s) count on joining a conference.</para>
109  </option>
110  <option name="C">
111  <para>Continue in dialplan when kicked out of conference.</para>
112  </option>
113  <option name="d">
114  <para>Dynamically add conference.</para>
115  </option>
116  <option name="D">
117  <para>Dynamically add conference, prompting for a PIN.</para>
118  </option>
119  <option name="e">
120  <para>Select an empty conference.</para>
121  </option>
122  <option name="E">
123  <para>Select an empty pinless conference.</para>
124  </option>
125  <option name="F">
126  <para>Pass DTMF through the conference.</para>
127  </option>
128  <option name="G">
129  <argument name="x" required="true">
130  <para>The file to playback</para>
131  </argument>
132  <para>Play an intro announcement in conference.</para>
133  </option>
134  <option name="i">
135  <para>Announce user join/leave with review.</para>
136  </option>
137  <option name="I">
138  <para>Announce user join/leave without review.</para>
139  </option>
140  <option name="k">
141  <para>Close the conference if there's only one active participant left at exit.</para>
142  </option>
143  <option name="l">
144  <para>Set listen only mode (Listen only, no talking).</para>
145  </option>
146  <option name="m">
147  <para>Set initially muted.</para>
148  </option>
149  <option name="M" hasparams="optional">
150  <para>Enable music on hold when the conference has a single caller. Optionally,
151  specify a musiconhold class to use. If one is not provided, it will use the
152  channel's currently set music class, or <literal>default</literal>.</para>
153  <argument name="class" required="true" />
154  </option>
155  <option name="n">
156  <para>Disable the denoiser. By default, if <literal>func_speex</literal> is loaded, Asterisk
157  will apply a denoiser to channels in the MeetMe conference. However, channel
158  drivers that present audio with a varying rate will experience degraded
159  performance with a denoiser attached. This parameter allows a channel joining
160  the conference to choose not to have a denoiser attached without having to
161  unload <literal>func_speex</literal>.</para>
162  </option>
163  <option name="o">
164  <para>Set talker optimization - treats talkers who aren't speaking as
165  being muted, meaning (a) No encode is done on transmission and (b)
166  Received audio that is not registered as talking is omitted causing no
167  buildup in background noise.</para>
168  </option>
169  <option name="p" hasparams="optional">
170  <para>Allow user to exit the conference by pressing <literal>#</literal> (default)
171  or any of the defined keys. Dial plan execution will continue at the next
172  priority following MeetMe. The key used is set to channel variable
173  <variable>MEETME_EXIT_KEY</variable>.</para>
174  <argument name="keys" required="true" />
175  <note>
176  <para>Option <literal>s</literal> has priority for <literal>*</literal>
177  since it cannot change its activation code.</para>
178  </note>
179  </option>
180  <option name="P">
181  <para>Always prompt for the pin even if it is specified.</para>
182  </option>
183  <option name="q">
184  <para>Quiet mode (don't play enter/leave sounds).</para>
185  </option>
186  <option name="r">
187  <para>Record conference (records as <variable>MEETME_RECORDINGFILE</variable>
188  using format <variable>MEETME_RECORDINGFORMAT</variable>. Default filename is
189  <literal>meetme-conf-rec-${CONFNO}-${UNIQUEID}</literal> and the default format is
190  wav.</para>
191  </option>
192  <option name="s">
193  <para>Present menu (user or admin) when <literal>*</literal> is received
194  (send to menu).</para>
195  </option>
196  <option name="t">
197  <para>Set talk only mode. (Talk only, no listening).</para>
198  </option>
199  <option name="T">
200  <para>Set talker detection (sent to manager interface and meetme list).</para>
201  </option>
202  <option name="v" hasparams="optional">
203  <para>Announce when a user is joining or leaving the conference. Use the voicemail greeting as the announcement.
204  If the i or I options are set, the application will fall back to them if no voicemail greeting can be found.</para>
205  <argument name="mailbox@[context]" required="true">
206  <para>The mailbox and voicemail context to play from. If no context provided, assumed context is default.</para>
207  </argument>
208  </option>
209  <option name="w" hasparams="optional">
210  <para>Wait until the marked user enters the conference.</para>
211  <argument name="secs" required="true" />
212  </option>
213  <option name="x">
214  <para>Leave the conference when the last marked user leaves.</para>
215  </option>
216  <option name="X">
217  <para>Allow user to exit the conference by entering a valid single digit
218  extension <variable>MEETME_EXIT_CONTEXT</variable> or the current context
219  if that variable is not defined.</para>
220  <note>
221  <para>Option <literal>s</literal> has priority for <literal>*</literal>
222  since it cannot change its activation code.</para>
223  </note>
224  </option>
225  <option name="1">
226  <para>Do not play message when first person enters</para>
227  </option>
228  <option name="S">
229  <para>Kick the user <replaceable>x</replaceable> seconds <emphasis>after</emphasis> he entered into
230  the conference.</para>
231  <argument name="x" required="true" />
232  </option>
233  <option name="L" argsep=":">
234  <para>Limit the conference to <replaceable>x</replaceable> ms. Play a warning when
235  <replaceable>y</replaceable> ms are left. Repeat the warning every <replaceable>z</replaceable> ms.
236  The following special variables can be used with this option:</para>
237  <variablelist>
238  <variable name="CONF_LIMIT_TIMEOUT_FILE">
239  <para>File to play when time is up.</para>
240  </variable>
241  <variable name="CONF_LIMIT_WARNING_FILE">
242  <para>File to play as warning if <replaceable>y</replaceable> is defined. The
243  default is to say the time remaining.</para>
244  </variable>
245  </variablelist>
246  <argument name="x" />
247  <argument name="y" />
248  <argument name="z" />
249  </option>
250  </optionlist>
251  </parameter>
252  <parameter name="pin" />
253  </syntax>
254  <description>
255  <para>Enters the user into a specified MeetMe conference. If the <replaceable>confno</replaceable>
256  is omitted, the user will be prompted to enter one. User can exit the conference by hangup, or
257  if the <literal>p</literal> option is specified, by pressing <literal>#</literal>.</para>
258  <note><para>The DAHDI kernel modules and a functional DAHDI timing source (see dahdi_test)
259  must be present for conferencing to operate properly. In addition, the chan_dahdi channel driver
260  must be loaded for the <literal>i</literal> and <literal>r</literal> options to operate at
261  all.</para></note>
262  </description>
263  <see-also>
264  <ref type="application">MeetMeCount</ref>
265  <ref type="application">MeetMeAdmin</ref>
266  <ref type="application">MeetMeChannelAdmin</ref>
267  </see-also>
268  </application>
269  <application name="MeetMeCount" language="en_US">
270  <synopsis>
271  MeetMe participant count.
272  </synopsis>
273  <syntax>
274  <parameter name="confno" required="true">
275  <para>Conference number.</para>
276  </parameter>
277  <parameter name="var" />
278  </syntax>
279  <description>
280  <para>Plays back the number of users in the specified MeetMe conference.
281  If <replaceable>var</replaceable> is specified, playback will be skipped and the value
282  will be returned in the variable. Upon application completion, MeetMeCount will hangup
283  the channel, unless priority <literal>n+1</literal> exists, in which case priority progress will
284  continue.</para>
285  </description>
286  <see-also>
287  <ref type="application">MeetMe</ref>
288  </see-also>
289  </application>
290  <application name="MeetMeAdmin" language="en_US">
291  <synopsis>
292  MeetMe conference administration.
293  </synopsis>
294  <syntax>
295  <parameter name="confno" required="true" />
296  <parameter name="command" required="true">
297  <optionlist>
298  <option name="e">
299  <para>Eject last user that joined.</para>
300  </option>
301  <option name="E">
302  <para>Extend conference end time, if scheduled.</para>
303  </option>
304  <option name="k">
305  <para>Kick one user out of conference.</para>
306  </option>
307  <option name="K">
308  <para>Kick all users out of conference.</para>
309  </option>
310  <option name="l">
311  <para>Unlock conference.</para>
312  </option>
313  <option name="L">
314  <para>Lock conference.</para>
315  </option>
316  <option name="m">
317  <para>Unmute one user.</para>
318  </option>
319  <option name="M">
320  <para>Mute one user.</para>
321  </option>
322  <option name="n">
323  <para>Unmute all users in the conference.</para>
324  </option>
325  <option name="N">
326  <para>Mute all non-admin users in the conference.</para>
327  </option>
328  <option name="r">
329  <para>Reset one user's volume settings.</para>
330  </option>
331  <option name="R">
332  <para>Reset all users volume settings.</para>
333  </option>
334  <option name="s">
335  <para>Lower entire conference speaking volume.</para>
336  </option>
337  <option name="S">
338  <para>Raise entire conference speaking volume.</para>
339  </option>
340  <option name="t">
341  <para>Lower one user's talk volume.</para>
342  </option>
343  <option name="T">
344  <para>Raise one user's talk volume.</para>
345  </option>
346  <option name="u">
347  <para>Lower one user's listen volume.</para>
348  </option>
349  <option name="U">
350  <para>Raise one user's listen volume.</para>
351  </option>
352  <option name="v">
353  <para>Lower entire conference listening volume.</para>
354  </option>
355  <option name="V">
356  <para>Raise entire conference listening volume.</para>
357  </option>
358  </optionlist>
359  </parameter>
360  <parameter name="user" />
361  </syntax>
362  <description>
363  <para>Run admin <replaceable>command</replaceable> for conference <replaceable>confno</replaceable>.</para>
364  <para>Will additionally set the variable <variable>MEETMEADMINSTATUS</variable> with one of
365  the following values:</para>
366  <variablelist>
367  <variable name="MEETMEADMINSTATUS">
368  <value name="NOPARSE">
369  Invalid arguments.
370  </value>
371  <value name="NOTFOUND">
372  User specified was not found.
373  </value>
374  <value name="FAILED">
375  Another failure occurred.
376  </value>
377  <value name="OK">
378  The operation was completed successfully.
379  </value>
380  </variable>
381  </variablelist>
382  </description>
383  <see-also>
384  <ref type="application">MeetMe</ref>
385  </see-also>
386  </application>
387  <application name="MeetMeChannelAdmin" language="en_US">
388  <synopsis>
389  MeetMe conference Administration (channel specific).
390  </synopsis>
391  <syntax>
392  <parameter name="channel" required="true" />
393  <parameter name="command" required="true">
394  <optionlist>
395  <option name="k">
396  <para>Kick the specified user out of the conference he is in.</para>
397  </option>
398  <option name="m">
399  <para>Unmute the specified user.</para>
400  </option>
401  <option name="M">
402  <para>Mute the specified user.</para>
403  </option>
404  </optionlist>
405  </parameter>
406  </syntax>
407  <description>
408  <para>Run admin <replaceable>command</replaceable> for a specific
409  <replaceable>channel</replaceable> in any conference.</para>
410  </description>
411  </application>
412  <application name="SLAStation" language="en_US">
413  <synopsis>
414  Shared Line Appearance Station.
415  </synopsis>
416  <syntax>
417  <parameter name="station" required="true">
418  <para>Station name</para>
419  </parameter>
420  </syntax>
421  <description>
422  <para>This application should be executed by an SLA station. The argument depends
423  on how the call was initiated. If the phone was just taken off hook, then the argument
424  <replaceable>station</replaceable> should be just the station name. If the call was
425  initiated by pressing a line key, then the station name should be preceded by an underscore
426  and the trunk name associated with that line button.</para>
427  <para>For example: <literal>station1_line1</literal></para>
428  <para>On exit, this application will set the variable <variable>SLASTATION_STATUS</variable> to
429  one of the following values:</para>
430  <variablelist>
431  <variable name="SLASTATION_STATUS">
432  <value name="FAILURE" />
433  <value name="CONGESTION" />
434  <value name="SUCCESS" />
435  </variable>
436  </variablelist>
437  </description>
438  </application>
439  <application name="SLATrunk" language="en_US">
440  <synopsis>
441  Shared Line Appearance Trunk.
442  </synopsis>
443  <syntax>
444  <parameter name="trunk" required="true">
445  <para>Trunk name</para>
446  </parameter>
447  <parameter name="options">
448  <optionlist>
449  <option name="M" hasparams="optional">
450  <para>Play back the specified MOH <replaceable>class</replaceable>
451  instead of ringing</para>
452  <argument name="class" required="true" />
453  </option>
454  </optionlist>
455  </parameter>
456  </syntax>
457  <description>
458  <para>This application should be executed by an SLA trunk on an inbound call. The channel calling
459  this application should correspond to the SLA trunk with the name <replaceable>trunk</replaceable>
460  that is being passed as an argument.</para>
461  <para>On exit, this application will set the variable <variable>SLATRUNK_STATUS</variable> to
462  one of the following values:</para>
463  <variablelist>
464  <variable name="SLATRUNK_STATUS">
465  <value name="FAILURE" />
466  <value name="SUCCESS" />
467  <value name="UNANSWERED" />
468  <value name="RINGTIMEOUT" />
469  </variable>
470  </variablelist>
471  </description>
472  </application>
473  <function name="MEETME_INFO" language="en_US">
474  <synopsis>
475  Query a given conference of various properties.
476  </synopsis>
477  <syntax>
478  <parameter name="keyword" required="true">
479  <para>Options:</para>
480  <enumlist>
481  <enum name="lock">
482  <para>Boolean of whether the corresponding conference is locked.</para>
483  </enum>
484  <enum name="parties">
485  <para>Number of parties in a given conference</para>
486  </enum>
487  <enum name="activity">
488  <para>Duration of conference in seconds.</para>
489  </enum>
490  <enum name="dynamic">
491  <para>Boolean of whether the corresponding conference is dynamic.</para>
492  </enum>
493  </enumlist>
494  </parameter>
495  <parameter name="confno" required="true">
496  <para>Conference number to retrieve information from.</para>
497  </parameter>
498  </syntax>
499  <description />
500  <see-also>
501  <ref type="application">MeetMe</ref>
502  <ref type="application">MeetMeCount</ref>
503  <ref type="application">MeetMeAdmin</ref>
504  <ref type="application">MeetMeChannelAdmin</ref>
505  </see-also>
506  </function>
507  <manager name="MeetmeMute" language="en_US">
508  <synopsis>
509  Mute a Meetme user.
510  </synopsis>
511  <syntax>
512  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
513  <parameter name="Meetme" required="true" />
514  <parameter name="Usernum" required="true" />
515  </syntax>
516  <description>
517  </description>
518  </manager>
519  <manager name="MeetmeUnmute" language="en_US">
520  <synopsis>
521  Unmute a Meetme user.
522  </synopsis>
523  <syntax>
524  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
525  <parameter name="Meetme" required="true" />
526  <parameter name="Usernum" required="true" />
527  </syntax>
528  <description>
529  </description>
530  </manager>
531  <manager name="MeetmeList" language="en_US">
532  <synopsis>
533  List participants in a conference.
534  </synopsis>
535  <syntax>
536  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
537  <parameter name="Conference" required="false">
538  <para>Conference number.</para>
539  </parameter>
540  </syntax>
541  <description>
542  <para>Lists all users in a particular MeetMe conference.
543  MeetmeList will follow as separate events, followed by a final event called
544  MeetmeListComplete.</para>
545  </description>
546  </manager>
547  <manager name="MeetmeListRooms" language="en_US">
548  <synopsis>
549  List active conferences.
550  </synopsis>
551  <syntax>
552  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
553  </syntax>
554  <description>
555  <para>Lists data about all active conferences.
556  MeetmeListRooms will follow as separate events, followed by a final event called
557  MeetmeListRoomsComplete.</para>
558  </description>
559  </manager>
560  <managerEvent language="en_US" name="MeetmeJoin">
561  <managerEventInstance class="EVENT_FLAG_CALL">
562  <synopsis>Raised when a user joins a MeetMe conference.</synopsis>
563  <syntax>
564  <parameter name="Meetme">
565  <para>The identifier for the MeetMe conference.</para>
566  </parameter>
567  <parameter name="User">
568  <para>The identifier of the MeetMe user who joined.</para>
569  </parameter>
570  <channel_snapshot/>
571  </syntax>
572  <see-also>
573  <ref type="managerEvent">MeetmeLeave</ref>
574  <ref type="application">MeetMe</ref>
575  </see-also>
576  </managerEventInstance>
577  </managerEvent>
578  <managerEvent language="en_US" name="MeetmeLeave">
579  <managerEventInstance class="EVENT_FLAG_CALL">
580  <synopsis>Raised when a user leaves a MeetMe conference.</synopsis>
581  <syntax>
582  <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter)" />
583  <channel_snapshot/>
584  <parameter name="Duration">
585  <para>The length of time in seconds that the Meetme user was in the conference.</para>
586  </parameter>
587  </syntax>
588  <see-also>
589  <ref type="managerEvent">MeetmeJoin</ref>
590  </see-also>
591  </managerEventInstance>
592  </managerEvent>
593  <managerEvent language="en_US" name="MeetmeEnd">
594  <managerEventInstance class="EVENT_FLAG_CALL">
595  <synopsis>Raised when a MeetMe conference ends.</synopsis>
596  <syntax>
597  <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
598  </syntax>
599  <see-also>
600  <ref type="managerEvent">MeetmeJoin</ref>
601  </see-also>
602  </managerEventInstance>
603  </managerEvent>
604  <managerEvent language="en_US" name="MeetmeTalkRequest">
605  <managerEventInstance class="EVENT_FLAG_CALL">
606  <synopsis>Raised when a MeetMe user has started talking.</synopsis>
607  <syntax>
608  <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter)" />
609  <channel_snapshot/>
610  <parameter name="Duration">
611  <para>The length of time in seconds that the Meetme user has been in the conference at the time of this event.</para>
612  </parameter>
613  <parameter name="Status">
614  <enumlist>
615  <enum name="on"/>
616  <enum name="off"/>
617  </enumlist>
618  </parameter>
619  </syntax>
620  </managerEventInstance>
621  </managerEvent>
622  <managerEvent language="en_US" name="MeetmeTalking">
623  <managerEventInstance class="EVENT_FLAG_CALL">
624  <synopsis>Raised when a MeetMe user begins or ends talking.</synopsis>
625  <syntax>
626  <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter)" />
627  <channel_snapshot/>
628  <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeTalkRequest']/managerEventInstance/syntax/parameter)" />
629  </syntax>
630  </managerEventInstance>
631  </managerEvent>
632  <managerEvent language="en_US" name="MeetmeMute">
633  <managerEventInstance class="EVENT_FLAG_CALL">
634  <synopsis>Raised when a MeetMe user is muted or unmuted.</synopsis>
635  <syntax>
636  <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter)" />
637  <channel_snapshot/>
638  <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeTalkRequest']/managerEventInstance/syntax/parameter)" />
639  </syntax>
640  </managerEventInstance>
641  </managerEvent>
642  ***/
643 
644 #define CONFIG_FILE_NAME "meetme.conf"
645 #define SLA_CONFIG_FILE "sla.conf"
646 #define STR_CONCISE "concise"
647 
648 /*! each buffer is 20ms, so this is 640ms total */
649 #define DEFAULT_AUDIO_BUFFERS 32
650 
651 /*! String format for scheduled conferences */
652 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
653 
654 enum {
655  ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
656  ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
657  ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */
658  /*! User has requested to speak */
660  ADMINFLAG_HANGUP = (1 << 5), /*!< User will be leaving the conference */
661 };
662 
663 #define MEETME_DELAYDETECTTALK 300
664 #define MEETME_DELAYDETECTENDTALK 1000
665 
666 #define AST_FRAME_BITS 32
667 
671 };
672 
676 };
677 
683 };
684 
685 #define CONF_SIZE 320
686 
687 enum {
688  /*! user has admin access on the conference */
689  CONFFLAG_ADMIN = (1 << 0),
690  /*! If set the user can only receive audio from the conference */
691  CONFFLAG_MONITOR = (1 << 1),
692  /*! If set asterisk will exit conference when key defined in p() option is pressed */
693  CONFFLAG_KEYEXIT = (1 << 2),
694  /*! If set asterisk will provide a menu to the user when '*' is pressed */
695  CONFFLAG_STARMENU = (1 << 3),
696  /*! If set the use can only send audio to the conference */
697  CONFFLAG_TALKER = (1 << 4),
698  /*! If set there will be no enter or leave sounds */
699  CONFFLAG_QUIET = (1 << 5),
700  /*! If set, when user joins the conference, they will be told the number
701  * of users that are already in */
703  /*! Set to run AGI Script in Background */
704  CONFFLAG_AGI = (1 << 7),
705  /*! Set to have music on hold when user is alone in conference */
706  CONFFLAG_MOH = (1 << 8),
707  /*! If set, the channel will leave the conference if all marked users leave */
709  /*! If set, the MeetMe will wait until a marked user enters */
710  CONFFLAG_WAITMARKED = (1 << 10),
711  /*! If set, the MeetMe will exit to the specified context */
713  /*! If set, the user will be marked */
714  CONFFLAG_MARKEDUSER = (1 << 12),
715  /*! If set, user will be ask record name on entry of conference */
716  CONFFLAG_INTROUSER = (1 << 13),
717  /*! If set, the MeetMe will be recorded */
719  /*! If set, the user will be monitored if the user is talking or not */
721  CONFFLAG_DYNAMIC = (1 << 16),
722  CONFFLAG_DYNAMICPIN = (1 << 17),
723  CONFFLAG_EMPTY = (1 << 18),
724  CONFFLAG_EMPTYNOPIN = (1 << 19),
726  /*! If set, treat talking users as muted users */
728  /*! If set, won't speak the extra prompt when the first person
729  * enters the conference */
731  /*! If set, user will be asked to record name on entry of conference
732  * without review */
734  /*! If set, the user will be initially self-muted */
735  CONFFLAG_STARTMUTED = (1 << 24),
736  /*! Pass DTMF through the conference */
737  CONFFLAG_PASS_DTMF = (1 << 25),
738  CONFFLAG_SLA_STATION = (1 << 26),
739  CONFFLAG_SLA_TRUNK = (1 << 27),
740  /*! If set, the user should continue in the dialplan if kicked out */
744 };
745 
746 /* These flags are defined separately because we ran out of bits that an enum can be used to represent.
747  If you add new flags, be sure to do it in the same way that these are. */
748 /*! Do not write any audio to this channel until the state is up. */
749 #define CONFFLAG_NO_AUDIO_UNTIL_UP (1ULL << 31)
750 #define CONFFLAG_INTROMSG (1ULL << 32) /*!< If set play an intro announcement at start of conference */
751 #define CONFFLAG_INTROUSER_VMREC (1ULL << 33)
752 /*! If there's only one person left in a conference when someone leaves, kill the conference */
753 #define CONFFLAG_KILL_LAST_MAN_STANDING (1ULL << 34)
754 /*! If set, don't enable a denoiser for the channel */
755 #define CONFFLAG_DONT_DENOISE (1ULL << 35)
756 
757 enum {
766 };
767 
803 
804 static const char * const app = "MeetMe";
805 static const char * const app2 = "MeetMeCount";
806 static const char * const app3 = "MeetMeAdmin";
807 static const char * const app4 = "MeetMeChannelAdmin";
808 static const char * const slastation_app = "SLAStation";
809 static const char * const slatrunk_app = "SLATrunk";
810 
811 /* Lookup RealTime conferences based on confno and current time */
812 static int rt_schedule;
813 static int fuzzystart;
814 static int earlyalert;
815 static int endalert;
816 static int extendby;
817 
818 /*! Log participant count to the RealTime backend */
819 static int rt_log_members;
820 
821 #define MAX_CONFNUM 80
822 #define MAX_PIN 80
823 #define OPTIONS_LEN 100
824 
825 /* Enough space for "<conference #>,<pin>,<admin pin>" followed by a 0 byte. */
826 #define MAX_SETTINGS (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)
827 
831 };
832 
835  char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
839  int vmrec;
841 };
842 
843 /*! \brief The MeetMe Conference object */
845  ast_mutex_t playlock; /*!< Conference specific lock (players) */
846  ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
847  char confno[MAX_CONFNUM]; /*!< Conference */
848  struct ast_channel *chan; /*!< Announcements channel */
849  struct ast_channel *lchan; /*!< Listen/Record channel */
850  int fd; /*!< Announcements fd */
851  int dahdiconf; /*!< DAHDI Conf # */
852  int users; /*!< Number of active users */
853  int markedusers; /*!< Number of marked users */
854  int maxusers; /*!< Participant limit if scheduled */
855  int endalert; /*!< When to play conf ending message */
856  time_t start; /*!< Start time (s) */
857  int refcount; /*!< reference count of usage */
858  enum recording_state recording:2; /*!< recording status */
859  unsigned int isdynamic:1; /*!< Created on the fly? */
860  unsigned int locked:1; /*!< Is the conference locked? */
861  unsigned int gmuted:1; /*!< Is the conference globally muted? (all non-admins) */
862  pthread_t recordthread; /*!< thread for recording */
863  ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
864  pthread_attr_t attr; /*!< thread attribute */
865  char *recordingfilename; /*!< Filename to record the Conference into */
866  char *recordingformat; /*!< Format to record the Conference in */
867  char pin[MAX_PIN]; /*!< If protected by a PIN */
868  char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
869  char uniqueid[32];
870  long endtime; /*!< When to end the conf if scheduled */
871  const char *useropts; /*!< RealTime user flags */
872  const char *adminopts; /*!< RealTime moderator flags */
873  const char *bookid; /*!< RealTime conference id */
874  struct ast_frame *transframe[32];
876  struct ast_trans_pvt *transpath[32];
879  /* announce_thread related data */
880  pthread_t announcethread;
882  unsigned int announcethread_stop:1;
886 };
887 
889 
890 static unsigned int conf_map[1024] = {0, };
891 
892 struct volume {
893  int desired; /*!< Desired volume adjustment */
894  int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
895 };
896 
897 /*! \brief The MeetMe User object */
899  int user_no; /*!< User Number */
900  struct ast_flags64 userflags; /*!< Flags as set in the conference */
901  int adminflags; /*!< Flags set by the Admin */
902  struct ast_channel *chan; /*!< Connected channel */
903  int talking; /*!< Is user talking */
904  int dahdichannel; /*!< Is a DAHDI channel */
905  char usrvalue[50]; /*!< Custom User Value */
906  char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
907  time_t jointime; /*!< Time the user joined the conference */
908  time_t kicktime; /*!< Time the user will be kicked from the conference */
909  struct timeval start_time; /*!< Time the user entered into the conference */
910  long timelimit; /*!< Time limit for the user to be in the conference L(x:y:z) */
911  long play_warning; /*!< Play a warning when 'y' ms are left */
912  long warning_freq; /*!< Repeat the warning every 'z' ms */
913  const char *warning_sound; /*!< File to play as warning if 'y' is defined */
914  const char *end_sound; /*!< File to play when time is up. */
915  struct volume talk;
916  struct volume listen;
918 };
919 
923 };
924 
931 };
932 
934  /*! This means that any station can put it on hold, and any station
935  * can retrieve the call from hold. */
937  /*! This means that only the station that put the call on hold may
938  * retrieve it from hold. */
940 };
941 
942 struct sla_trunk_ref;
943 
944 struct sla_station {
948  AST_STRING_FIELD(device);
949  AST_STRING_FIELD(autocontext);
950  );
952  struct ast_dial *dial;
953  /*! Ring timeout for this station, for any trunk. If a ring timeout
954  * is set for a specific trunk on this station, that will take
955  * priority over this value. */
956  unsigned int ring_timeout;
957  /*! Ring delay for this station, for any trunk. If a ring delay
958  * is set for a specific trunk on this station, that will take
959  * priority over this value. */
960  unsigned int ring_delay;
961  /*! This option uses the values in the sla_hold_access enum and sets the
962  * access control type for hold on this station. */
963  unsigned int hold_access:1;
964  /*! Mark used during reload processing */
965  unsigned int mark:1;
966 };
967 
968 /*!
969  * \brief A reference to a station
970  *
971  * This struct looks near useless at first glance. However, its existence
972  * in the list of stations in sla_trunk means that this station references
973  * that trunk. We use the mark to keep track of whether it needs to be
974  * removed from the sla_trunk's list of stations during a reload.
975  */
979  /*! Mark used during reload processing */
980  unsigned int mark:1;
981 };
982 
983 struct sla_trunk {
986  AST_STRING_FIELD(device);
987  AST_STRING_FIELD(autocontext);
988  );
990  /*! Number of stations that use this trunk */
991  unsigned int num_stations;
992  /*! Number of stations currently on a call with this trunk */
993  unsigned int active_stations;
994  /*! Number of stations that have this trunk on hold. */
995  unsigned int hold_stations;
996  struct ast_channel *chan;
997  unsigned int ring_timeout;
998  /*! If set to 1, no station will be able to join an active call with
999  * this trunk. */
1000  unsigned int barge_disabled:1;
1001  /*! This option uses the values in the sla_hold_access enum and sets the
1002  * access control type for hold on this trunk. */
1003  unsigned int hold_access:1;
1004  /*! Whether this trunk is currently on hold, meaning that once a station
1005  * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
1006  unsigned int on_hold:1;
1007  /*! Mark used during reload processing */
1008  unsigned int mark:1;
1009 };
1010 
1011 /*!
1012  * \brief A station's reference to a trunk
1013  *
1014  * An sla_station keeps a list of trunk_refs. This holds metadata about the
1015  * stations usage of the trunk.
1016  */
1019  struct sla_trunk *trunk;
1022  /*! Ring timeout to use when this trunk is ringing on this specific
1023  * station. This takes higher priority than a ring timeout set at
1024  * the station level. */
1025  unsigned int ring_timeout;
1026  /*! Ring delay to use when this trunk is ringing on this specific
1027  * station. This takes higher priority than a ring delay set at
1028  * the station level. */
1029  unsigned int ring_delay;
1030  /*! Mark used during reload processing */
1031  unsigned int mark:1;
1032 };
1033 
1035 static struct ao2_container *sla_trunks;
1036 
1037 static const char sla_registrar[] = "SLA";
1038 
1039 /*! \brief Event types that can be queued up for the SLA thread */
1041  /*! A station has put the call on hold */
1043  /*! The state of a dial has changed */
1045  /*! The state of a ringing trunk has changed */
1047 };
1048 
1049 struct sla_event {
1054 };
1055 
1056 /*! \brief A station that failed to be dialed
1057  * \note Only used by the SLA thread. */
1060  struct timeval last_try;
1062 };
1063 
1064 /*! \brief A trunk that is ringing */
1066  struct sla_trunk *trunk;
1067  /*! The time that this trunk started ringing */
1068  struct timeval ring_begin;
1069  AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
1071 };
1072 
1076 };
1077 
1078 /*! \brief A station that is ringing */
1081  /*! The time that this station started ringing */
1082  struct timeval ring_begin;
1084 };
1085 
1086 /*!
1087  * \brief A structure for data used by the sla thread
1088  */
1089 static struct {
1090  /*! The SLA thread ID */
1091  pthread_t thread;
1098  unsigned int stop:1;
1099  /*! Attempt to handle CallerID, even though it is known not to work
1100  * properly in some situations. */
1101  unsigned int attempt_callerid:1;
1102 } sla = {
1103  .thread = AST_PTHREADT_NULL,
1104 };
1105 
1106 /*! \brief The number of audio buffers to be allocated on pseudo channels
1107  * when in a conference */
1108 static int audio_buffers;
1109 
1110 /*! \brief Map 'volume' levels from -5 through +5 into decibel (dB)
1111  * settings for channel drivers.
1112  *
1113  * \note these are not a straight linear-to-dB
1114  * conversion... the numbers have been modified
1115  * to give the user a better level of adjustability.
1116  */
1117 static const char gain_map[] = {
1118  -15,
1119  -13,
1120  -10,
1121  -6,
1122  0,
1123  0,
1124  0,
1125  6,
1126  10,
1127  13,
1128  15,
1129 };
1130 
1131 /* Routes the various meetme message types to the meetme stasis callback function to turn them into events */
1133 
1134 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_join_type);
1135 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_leave_type);
1136 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_end_type);
1137 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_mute_type);
1138 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_talking_type);
1139 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_talk_request_type);
1140 
1141 static void meetme_stasis_cb(void *data, struct stasis_subscription *sub,
1142  struct stasis_message *message);
1143 
1144 static void meetme_stasis_cleanup(void)
1145 {
1146  if (meetme_event_message_router) {
1147  stasis_message_router_unsubscribe(meetme_event_message_router);
1148  meetme_event_message_router = NULL;
1149  }
1150 
1151  STASIS_MESSAGE_TYPE_CLEANUP(meetme_join_type);
1152  STASIS_MESSAGE_TYPE_CLEANUP(meetme_leave_type);
1153  STASIS_MESSAGE_TYPE_CLEANUP(meetme_end_type);
1154  STASIS_MESSAGE_TYPE_CLEANUP(meetme_mute_type);
1155  STASIS_MESSAGE_TYPE_CLEANUP(meetme_talking_type);
1156  STASIS_MESSAGE_TYPE_CLEANUP(meetme_talk_request_type);
1157 }
1158 
1159 static int meetme_stasis_init(void)
1160 {
1161 
1162  STASIS_MESSAGE_TYPE_INIT(meetme_join_type);
1163  STASIS_MESSAGE_TYPE_INIT(meetme_leave_type);
1164  STASIS_MESSAGE_TYPE_INIT(meetme_end_type);
1165  STASIS_MESSAGE_TYPE_INIT(meetme_mute_type);
1166  STASIS_MESSAGE_TYPE_INIT(meetme_talking_type);
1167  STASIS_MESSAGE_TYPE_INIT(meetme_talk_request_type);
1168 
1169  meetme_event_message_router = stasis_message_router_create(
1171 
1172  if (!meetme_event_message_router) {
1174  return -1;
1175  }
1176 
1177  if (stasis_message_router_add(meetme_event_message_router,
1178  meetme_join_type(),
1180  NULL)) {
1182  return -1;
1183  }
1184 
1185  if (stasis_message_router_add(meetme_event_message_router,
1186  meetme_leave_type(),
1188  NULL)) {
1190  return -1;
1191  }
1192 
1193  if (stasis_message_router_add(meetme_event_message_router,
1194  meetme_end_type(),
1196  NULL)) {
1198  return -1;
1199  }
1200 
1201  if (stasis_message_router_add(meetme_event_message_router,
1202  meetme_mute_type(),
1204  NULL)) {
1206  return -1;
1207  }
1208 
1209  if (stasis_message_router_add(meetme_event_message_router,
1210  meetme_talking_type(),
1212  NULL)) {
1214  return -1;
1215  }
1216 
1217  if (stasis_message_router_add(meetme_event_message_router,
1218  meetme_talk_request_type(),
1220  NULL)) {
1222  return -1;
1223  }
1224 
1225  return 0;
1226 }
1227 
1228 static void meetme_stasis_cb(void *data, struct stasis_subscription *sub,
1229  struct stasis_message *message)
1230 {
1231  struct ast_channel_blob *channel_blob = stasis_message_data(message);
1232  struct stasis_message_type *message_type;
1233  const char *event;
1234  const char *conference_num;
1235  const char *status;
1236  struct ast_json *json_cur;
1237  RAII_VAR(struct ast_str *, channel_text, NULL, ast_free);
1238  RAII_VAR(struct ast_str *, extra_text, NULL, ast_free);
1239 
1240  if (!channel_blob) {
1241  ast_assert(0);
1242  return;
1243  }
1244 
1245  message_type = stasis_message_type(message);
1246 
1247  if (!message_type) {
1248  ast_assert(0);
1249  return;
1250  }
1251 
1252  if (message_type == meetme_join_type()) {
1253  event = "MeetmeJoin";
1254  } else if (message_type == meetme_leave_type()) {
1255  event = "MeetmeLeave";
1256  } else if (message_type == meetme_end_type()) {
1257  event = "MeetmeEnd";
1258  } else if (message_type == meetme_mute_type()) {
1259  event = "MeetmeMute";
1260  } else if (message_type == meetme_talking_type()) {
1261  event = "MeetmeTalking";
1262  } else if (message_type == meetme_talk_request_type()) {
1263  event = "MeetmeTalkRequest";
1264  } else {
1265  ast_assert(0);
1266  return;
1267  }
1268 
1269  if (!event) {
1270  ast_assert(0);
1271  return;
1272  }
1273 
1274  conference_num = ast_json_string_get(ast_json_object_get(channel_blob->blob, "Meetme"));
1275  if (!conference_num) {
1276  ast_assert(0);
1277  return;
1278  }
1279 
1280  status = ast_json_string_get(ast_json_object_get(channel_blob->blob, "status"));
1281  if (status) {
1282  ast_str_append_event_header(&extra_text, "Status", status);
1283  }
1284 
1285  if (channel_blob->snapshot) {
1286  channel_text = ast_manager_build_channel_state_string(channel_blob->snapshot);
1287  }
1288 
1289  if ((json_cur = ast_json_object_get(channel_blob->blob, "user"))) {
1290  int user_number = ast_json_integer_get(json_cur);
1291  RAII_VAR(struct ast_str *, user_prop_str, ast_str_create(32), ast_free);
1292  if (!user_prop_str) {
1293  return;
1294  }
1295 
1296  ast_str_set(&user_prop_str, 0, "%d", user_number);
1297  ast_str_append_event_header(&extra_text, "User", ast_str_buffer(user_prop_str));
1298 
1299  if ((json_cur = ast_json_object_get(channel_blob->blob, "duration"))) {
1300  int duration = ast_json_integer_get(json_cur);
1301  ast_str_set(&user_prop_str, 0, "%d", duration);
1302  ast_str_append_event_header(&extra_text, "Duration", ast_str_buffer(user_prop_str));
1303  }
1304 
1305  json_cur = NULL;
1306  }
1307 
1309  "Meetme: %s\r\n"
1310  "%s"
1311  "%s",
1312  conference_num,
1313  channel_text ? ast_str_buffer(channel_text) : "",
1314  extra_text ? ast_str_buffer(extra_text) : "");
1315 }
1316 
1317 /*!
1318  * \internal
1319  * \brief Build a json object from a status value for inclusion in json extras for meetme_stasis_generate_msg
1320  * \since 12.0.0
1321  *
1322  * \param on if true, then status is on. Otherwise status is off
1323  * \retval NULL on failure to allocate the JSON blob.
1324  * \retval pointer to the JSON blob if successful.
1325  */
1326 static struct ast_json *status_to_json(int on)
1327 {
1328  struct ast_json *json_object = ast_json_pack("{s: s}",
1329  "status", on ? "on" : "off");
1330 
1331  return json_object;
1332 }
1333 
1334 /*!
1335  * \internal
1336  * \brief Generate a stasis message associated with a meetme event
1337  * \since 12.0.0
1338  *
1339  * \param meetme_confere The conference responsible for generating this message
1340  * \param chan The channel involved in the message (NULL allowed)
1341  * \param user The conference user involved in the message (NULL allowed)
1342  * \param message_type the type the stasis message being generated
1343  * \param extras Additional json fields desired for inclusion
1344  */
1345 static void meetme_stasis_generate_msg(struct ast_conference *meetme_conference, struct ast_channel *chan,
1346  struct ast_conf_user *user, struct stasis_message_type *message_type, struct ast_json *extras)
1347 {
1348  RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
1349  RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
1350 
1351  json_object = ast_json_pack("{s: s}",
1352  "Meetme", meetme_conference->confno);
1353 
1354  if (!json_object) {
1355  return;
1356  }
1357 
1358  if (extras) {
1359  ast_json_object_update(json_object, extras);
1360  }
1361 
1362  if (user) {
1363  struct timeval now = ast_tvnow();
1364  long duration = (long)(now.tv_sec - user->jointime);
1365  struct ast_json *json_user;
1366  struct ast_json *json_user_duration;
1367 
1368  json_user = ast_json_integer_create(user->user_no);
1369  if (!json_user || ast_json_object_set(json_object, "user", json_user)) {
1370  return;
1371  }
1372 
1373  if (duration > 0) {
1374  json_user_duration = ast_json_integer_create(duration);
1375  if (!json_user_duration
1376  || ast_json_object_set(json_object, "duration", json_user_duration)) {
1377  return;
1378  }
1379  }
1380  }
1381 
1382  if (chan) {
1383  ast_channel_lock(chan);
1384  }
1385  msg = ast_channel_blob_create(chan, message_type, json_object);
1386  if (chan) {
1387  ast_channel_unlock(chan);
1388  }
1389 
1390  if (!msg) {
1391  return;
1392  }
1393 
1394  stasis_publish(ast_channel_topic(chan), msg);
1395 }
1396 
1397 static int admin_exec(struct ast_channel *chan, const char *data);
1398 static void *recordthread(void *args);
1399 
1400 static const char *istalking(int x)
1401 {
1402  if (x > 0)
1403  return "(talking)";
1404  else if (x < 0)
1405  return "(unmonitored)";
1406  else
1407  return "(not talking)";
1408 }
1409 
1410 static int careful_write(int fd, unsigned char *data, int len, int block)
1411 {
1412  int res;
1413  int x;
1414 
1415  while (len) {
1416  if (block) {
1417  x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
1418  res = ioctl(fd, DAHDI_IOMUX, &x);
1419  } else
1420  res = 0;
1421  if (res >= 0)
1422  res = write(fd, data, len);
1423  if (res < 1) {
1424  if (errno != EAGAIN) {
1425  ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
1426  return -1;
1427  } else
1428  return 0;
1429  }
1430  len -= res;
1431  data += res;
1432  }
1433 
1434  return 0;
1435 }
1436 
1437 static int set_talk_volume(struct ast_conf_user *user, int volume)
1438 {
1439  char gain_adjust;
1440 
1441  /* attempt to make the adjustment in the channel driver;
1442  if successful, don't adjust in the frame reading routine
1443  */
1444  gain_adjust = gain_map[volume + 5];
1445 
1446  return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1447 }
1448 
1449 static int set_listen_volume(struct ast_conf_user *user, int volume)
1450 {
1451  char gain_adjust;
1452 
1453  /* attempt to make the adjustment in the channel driver;
1454  if successful, don't adjust in the frame reading routine
1455  */
1456  gain_adjust = gain_map[volume + 5];
1457 
1458  return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1459 }
1460 
1461 static void tweak_volume(struct volume *vol, enum volume_action action)
1462 {
1463  switch (action) {
1464  case VOL_UP:
1465  switch (vol->desired) {
1466  case 5:
1467  break;
1468  case 0:
1469  vol->desired = 2;
1470  break;
1471  case -2:
1472  vol->desired = 0;
1473  break;
1474  default:
1475  vol->desired++;
1476  break;
1477  }
1478  break;
1479  case VOL_DOWN:
1480  switch (vol->desired) {
1481  case -5:
1482  break;
1483  case 2:
1484  vol->desired = 0;
1485  break;
1486  case 0:
1487  vol->desired = -2;
1488  break;
1489  default:
1490  vol->desired--;
1491  break;
1492  }
1493  }
1494 }
1495 
1496 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
1497 {
1498  tweak_volume(&user->talk, action);
1499  /* attempt to make the adjustment in the channel driver;
1500  if successful, don't adjust in the frame reading routine
1501  */
1502  if (!set_talk_volume(user, user->talk.desired))
1503  user->talk.actual = 0;
1504  else
1505  user->talk.actual = user->talk.desired;
1506 }
1507 
1508 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
1509 {
1510  tweak_volume(&user->listen, action);
1511  /* attempt to make the adjustment in the channel driver;
1512  if successful, don't adjust in the frame reading routine
1513  */
1514  if (!set_listen_volume(user, user->listen.desired))
1515  user->listen.actual = 0;
1516  else
1517  user->listen.actual = user->listen.desired;
1518 }
1519 
1520 static void reset_volumes(struct ast_conf_user *user)
1521 {
1522  signed char zero_volume = 0;
1523 
1524  ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
1525  ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
1526 }
1527 
1528 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
1529 {
1530  unsigned char *data;
1531  int len;
1532  int res = -1;
1533 
1534  ast_test_suite_event_notify("CONFPLAY", "Channel: %s\r\n"
1535  "Conference: %s\r\n"
1536  "Marked: %d",
1537  ast_channel_name(chan),
1538  conf->confno,
1539  conf->markedusers);
1540 
1541  if (!ast_check_hangup(chan))
1542  res = ast_autoservice_start(chan);
1543 
1544  AST_LIST_LOCK(&confs);
1545 
1546  switch(sound) {
1547  case ENTER:
1548  data = enter;
1549  len = sizeof(enter);
1550  break;
1551  case LEAVE:
1552  data = leave;
1553  len = sizeof(leave);
1554  break;
1555  default:
1556  data = NULL;
1557  len = 0;
1558  }
1559  if (data) {
1560  careful_write(conf->fd, data, len, 1);
1561  }
1562 
1564 
1565  if (!res)
1566  ast_autoservice_stop(chan);
1567 }
1568 
1569 static int user_no_cmp(void *obj, void *arg, int flags)
1570 {
1571  struct ast_conf_user *user = obj;
1572  int *user_no = arg;
1573 
1574  if (user->user_no == *user_no) {
1575  return (CMP_MATCH | CMP_STOP);
1576  }
1577 
1578  return 0;
1579 }
1580 
1581 static int user_max_cmp(void *obj, void *arg, int flags)
1582 {
1583  struct ast_conf_user *user = obj;
1584  int *max_no = arg;
1585 
1586  if (user->user_no > *max_no) {
1587  *max_no = user->user_no;
1588  }
1589 
1590  return 0;
1591 }
1592 
1593 /*!
1594  * \brief Find or create a conference
1595  *
1596  * \param confno The conference name/number
1597  * \param pin The regular user pin
1598  * \param pinadmin The admin pin
1599  * \param make Make the conf if it doesn't exist
1600  * \param dynamic Mark the newly created conference as dynamic
1601  * \param refcount How many references to mark on the conference
1602  * \param chan The asterisk channel
1603  * \param test
1604  *
1605  * \return A pointer to the conference struct, or NULL if it wasn't found and
1606  * make or dynamic were not set.
1607  */
1608 static struct ast_conference *build_conf(const char *confno, const char *pin,
1609  const char *pinadmin, int make, int dynamic, int refcount,
1610  const struct ast_channel *chan, struct ast_test *test)
1611 {
1612  struct ast_conference *cnf;
1613  struct dahdi_confinfo dahdic = { 0, };
1614  int confno_int = 0;
1616 
1617  AST_LIST_LOCK(&confs);
1618 
1619  AST_LIST_TRAVERSE(&confs, cnf, list) {
1620  if (!strcmp(confno, cnf->confno))
1621  break;
1622  }
1623 
1624  if (cnf || (!make && !dynamic) || !cap_slin)
1625  goto cnfout;
1626 
1627  ast_format_cap_append(cap_slin, ast_format_slin, 0);
1628  /* Make a new one */
1629  cnf = ast_calloc(1, sizeof(*cnf));
1630  if (!cnf) {
1631  goto cnfout;
1632  }
1633 
1635  NULL, user_no_cmp);
1636  if (!cnf->usercontainer) {
1637  goto cnfout;
1638  }
1639 
1640  ast_mutex_init(&cnf->playlock);
1641  ast_mutex_init(&cnf->listenlock);
1646  ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
1647  ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
1648  ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
1649  ast_copy_string(cnf->uniqueid, ast_channel_uniqueid(chan), sizeof(cnf->uniqueid));
1650 
1651  /* Setup a new dahdi conference */
1652  dahdic.confno = -1;
1653  dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1654  cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
1655  if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
1656  if (test) {
1657  /* if we are creating a conference for a unit test, it is not neccesary
1658  * to open a pseudo channel, so, if we fail continue creating
1659  * the conference. */
1660  ast_test_status_update(test, "Unable to open DAHDI pseudo device\n");
1661  } else {
1662  ast_log(LOG_WARNING, "Unable to open DAHDI pseudo device\n");
1663  if (cnf->fd >= 0)
1664  close(cnf->fd);
1665  ao2_ref(cnf->usercontainer, -1);
1666  ast_mutex_destroy(&cnf->playlock);
1670  ast_free(cnf);
1671  cnf = NULL;
1672  goto cnfout;
1673  }
1674  }
1675 
1676  cnf->dahdiconf = dahdic.confno;
1677 
1678  /* Setup a new channel for playback of audio files */
1679  cnf->chan = ast_request("DAHDI", cap_slin, NULL, chan, "pseudo", NULL);
1680  if (cnf->chan) {
1683  dahdic.chan = 0;
1684  dahdic.confno = cnf->dahdiconf;
1685  dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1686  if (ioctl(ast_channel_fd(cnf->chan, 0), DAHDI_SETCONF, &dahdic)) {
1687  if (test) {
1688  ast_test_status_update(test, "Error setting conference on pseudo channel\n");
1689  }
1690  ast_log(LOG_WARNING, "Error setting conference\n");
1691  if (cnf->chan)
1692  ast_hangup(cnf->chan);
1693  else
1694  close(cnf->fd);
1695  ao2_ref(cnf->usercontainer, -1);
1696  ast_mutex_destroy(&cnf->playlock);
1700  ast_free(cnf);
1701  cnf = NULL;
1702  goto cnfout;
1703  }
1704  }
1705 
1706  /* Fill the conference struct */
1707  cnf->start = time(NULL);
1708  cnf->maxusers = 0x7fffffff;
1709  cnf->isdynamic = dynamic ? 1 : 0;
1710  ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
1711  AST_LIST_INSERT_HEAD(&confs, cnf, list);
1712 
1713  /* Reserve conference number in map */
1714  if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1715  conf_map[confno_int] = 1;
1716 
1717 cnfout:
1718  ao2_cleanup(cap_slin);
1719  if (cnf)
1720  ast_atomic_fetchadd_int(&cnf->refcount, refcount);
1721 
1723 
1724  return cnf;
1725 }
1726 
1727 static char *complete_confno(const char *word, int state)
1728 {
1729  struct ast_conference *cnf;
1730  char *ret = NULL;
1731  int which = 0;
1732  int len = strlen(word);
1733 
1734  AST_LIST_LOCK(&confs);
1735  AST_LIST_TRAVERSE(&confs, cnf, list) {
1736  if (!strncmp(word, cnf->confno, len) && ++which > state) {
1737  /* dup before releasing the lock */
1738  ret = ast_strdup(cnf->confno);
1739  break;
1740  }
1741  }
1743  return ret;
1744 }
1745 
1746 static char *complete_userno(struct ast_conference *cnf, const char *word, int state)
1747 {
1748  char usrno[50];
1749  struct ao2_iterator iter;
1750  struct ast_conf_user *usr;
1751  char *ret = NULL;
1752  int which = 0;
1753  int len = strlen(word);
1754 
1755  iter = ao2_iterator_init(cnf->usercontainer, 0);
1756  for (; (usr = ao2_iterator_next(&iter)); ao2_ref(usr, -1)) {
1757  snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
1758  if (!strncmp(word, usrno, len) && ++which > state) {
1759  ao2_ref(usr, -1);
1760  ret = ast_strdup(usrno);
1761  break;
1762  }
1763  }
1764  ao2_iterator_destroy(&iter);
1765  return ret;
1766 }
1767 
1768 static char *complete_meetmecmd_mute_kick(const char *line, const char *word, int pos, int state)
1769 {
1770  if (pos == 2) {
1771  return complete_confno(word, state);
1772  }
1773  if (pos == 3) {
1774  int len = strlen(word);
1775  char *ret = NULL;
1776  char *saved = NULL;
1777  char *myline;
1778  char *confno;
1779  struct ast_conference *cnf;
1780 
1781  if (!strncasecmp(word, "all", len)) {
1782  if (state == 0) {
1783  return ast_strdup("all");
1784  }
1785  --state;
1786  }
1787 
1788  /* Extract the confno from the command line. */
1789  myline = ast_strdupa(line);
1790  strtok_r(myline, " ", &saved);
1791  strtok_r(NULL, " ", &saved);
1792  confno = strtok_r(NULL, " ", &saved);
1793 
1794  AST_LIST_LOCK(&confs);
1795  AST_LIST_TRAVERSE(&confs, cnf, list) {
1796  if (!strcmp(confno, cnf->confno)) {
1797  ret = complete_userno(cnf, word, state);
1798  break;
1799  }
1800  }
1802 
1803  return ret;
1804  }
1805  return NULL;
1806 }
1807 
1808 static char *complete_meetmecmd_lock(const char *word, int pos, int state)
1809 {
1810  if (pos == 2) {
1811  return complete_confno(word, state);
1812  }
1813  return NULL;
1814 }
1815 
1816 static char *complete_meetmecmd_list(const char *line, const char *word, int pos, int state)
1817 {
1818  int len;
1819 
1820  if (pos == 2) {
1821  len = strlen(word);
1822  if (!strncasecmp(word, STR_CONCISE, len)) {
1823  if (state == 0) {
1824  return ast_strdup(STR_CONCISE);
1825  }
1826  --state;
1827  }
1828 
1829  return complete_confno(word, state);
1830  }
1831  if (pos == 3 && state == 0) {
1832  char *saved = NULL;
1833  char *myline;
1834  char *confno;
1835 
1836  /* Extract the confno from the command line. */
1837  myline = ast_strdupa(line);
1838  strtok_r(myline, " ", &saved);
1839  strtok_r(NULL, " ", &saved);
1840  confno = strtok_r(NULL, " ", &saved);
1841 
1842  if (!strcasecmp(confno, STR_CONCISE)) {
1843  /* There is nothing valid in this position now. */
1844  return NULL;
1845  }
1846 
1847  len = strlen(word);
1848  if (!strncasecmp(word, STR_CONCISE, len)) {
1849  return ast_strdup(STR_CONCISE);
1850  }
1851  }
1852  return NULL;
1853 }
1854 
1855 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1856 {
1857  /* Process the command */
1858  struct ast_conf_user *user;
1859  struct ast_conference *cnf;
1860  int hr, min, sec;
1861  int total = 0;
1862  time_t now;
1863 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
1864 #define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
1865 
1866  switch (cmd) {
1867  case CLI_INIT:
1868  e->command = "meetme list";
1869  e->usage =
1870  "Usage: meetme list [<confno>] [" STR_CONCISE "]\n"
1871  " List all conferences or a specific conference.\n";
1872  return NULL;
1873  case CLI_GENERATE:
1874  return complete_meetmecmd_list(a->line, a->word, a->pos, a->n);
1875  }
1876 
1877  if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], STR_CONCISE))) {
1878  /* List all the conferences */
1879  int concise = (a->argc == 3);
1880  struct ast_str *marked_users;
1881 
1882  if (!(marked_users = ast_str_create(30))) {
1883  return CLI_FAILURE;
1884  }
1885 
1886  now = time(NULL);
1887  AST_LIST_LOCK(&confs);
1888  if (AST_LIST_EMPTY(&confs)) {
1889  if (!concise) {
1890  ast_cli(a->fd, "No active MeetMe conferences.\n");
1891  }
1893  ast_free(marked_users);
1894  return CLI_SUCCESS;
1895  }
1896  if (!concise) {
1897  ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
1898  }
1899  AST_LIST_TRAVERSE(&confs, cnf, list) {
1900  hr = (now - cnf->start) / 3600;
1901  min = ((now - cnf->start) % 3600) / 60;
1902  sec = (now - cnf->start) % 60;
1903  if (!concise) {
1904  if (cnf->markedusers == 0) {
1905  ast_str_set(&marked_users, 0, "N/A ");
1906  } else {
1907  ast_str_set(&marked_users, 0, "%4.4d", cnf->markedusers);
1908  }
1909  ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users,
1910  ast_str_buffer(marked_users), hr, min, sec,
1911  cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
1912  } else {
1913  ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1914  cnf->confno,
1915  cnf->users,
1916  cnf->markedusers,
1917  hr, min, sec,
1918  cnf->isdynamic,
1919  cnf->locked);
1920  }
1921 
1922  total += cnf->users;
1923  }
1925  if (!concise) {
1926  ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1927  }
1928  ast_free(marked_users);
1929  return CLI_SUCCESS;
1930  }
1931  if (a->argc == 3 || (a->argc == 4 && !strcasecmp(a->argv[3], STR_CONCISE))) {
1932  struct ao2_iterator user_iter;
1933  int concise = (a->argc == 4);
1934 
1935  /* List all the users in a conference */
1936  if (AST_LIST_EMPTY(&confs)) {
1937  if (!concise) {
1938  ast_cli(a->fd, "No active MeetMe conferences.\n");
1939  }
1940  return CLI_SUCCESS;
1941  }
1942  /* Find the right conference */
1943  AST_LIST_LOCK(&confs);
1944  AST_LIST_TRAVERSE(&confs, cnf, list) {
1945  if (strcmp(cnf->confno, a->argv[2]) == 0) {
1946  break;
1947  }
1948  }
1949  if (!cnf) {
1950  if (!concise)
1951  ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
1953  return CLI_SUCCESS;
1954  }
1955  /* Show all the users */
1956  time(&now);
1957  user_iter = ao2_iterator_init(cnf->usercontainer, 0);
1958  while((user = ao2_iterator_next(&user_iter))) {
1959  hr = (now - user->jointime) / 3600;
1960  min = ((now - user->jointime) % 3600) / 60;
1961  sec = (now - user->jointime) % 60;
1962  if (!concise) {
1963  ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1964  user->user_no,
1965  S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
1966  S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<no name>"),
1967  ast_channel_name(user->chan),
1968  ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "(Admin)" : "",
1969  ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "(Listen only)" : "",
1970  user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1971  user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1972  istalking(user->talking), hr, min, sec);
1973  } else {
1974  ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1975  user->user_no,
1978  ast_channel_name(user->chan),
1979  ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "1" : "",
1980  ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "1" : "",
1981  user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1982  user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1983  user->talking, hr, min, sec);
1984  }
1985  ao2_ref(user, -1);
1986  }
1987  ao2_iterator_destroy(&user_iter);
1988  if (!concise) {
1989  ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
1990  }
1992  return CLI_SUCCESS;
1993  }
1994  return CLI_SHOWUSAGE;
1995 }
1996 
1997 
1998 static char *meetme_cmd_helper(struct ast_cli_args *a)
1999 {
2000  /* Process the command */
2001  struct ast_str *cmdline;
2002 
2003  /* Max confno length */
2004  if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
2005  return CLI_FAILURE;
2006  }
2007 
2008  ast_str_set(&cmdline, 0, "%s", a->argv[2]); /* Argv 2: conference number */
2009  if (strcasestr(a->argv[1], "lock")) {
2010  if (strcasecmp(a->argv[1], "lock") == 0) {
2011  /* Lock */
2012  ast_str_append(&cmdline, 0, ",L");
2013  } else {
2014  /* Unlock */
2015  ast_str_append(&cmdline, 0, ",l");
2016  }
2017  } else if (strcasestr(a->argv[1], "mute")) {
2018  if (strcasecmp(a->argv[1], "mute") == 0) {
2019  /* Mute */
2020  if (strcasecmp(a->argv[3], "all") == 0) {
2021  ast_str_append(&cmdline, 0, ",N");
2022  } else {
2023  ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);
2024  }
2025  } else {
2026  /* Unmute */
2027  if (strcasecmp(a->argv[3], "all") == 0) {
2028  ast_str_append(&cmdline, 0, ",n");
2029  } else {
2030  ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
2031  }
2032  }
2033  } else if (strcasecmp(a->argv[1], "kick") == 0) {
2034  if (strcasecmp(a->argv[3], "all") == 0) {
2035  /* Kick all */
2036  ast_str_append(&cmdline, 0, ",K");
2037  } else {
2038  /* Kick a single user */
2039  ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
2040  }
2041  } else {
2042  /*
2043  * Should never get here because it is already filtered by the
2044  * callers.
2045  */
2046  ast_free(cmdline);
2047  return CLI_SHOWUSAGE;
2048  }
2049 
2050  ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
2051 
2052  admin_exec(NULL, ast_str_buffer(cmdline));
2053  ast_free(cmdline);
2054 
2055  return CLI_SUCCESS;
2056 }
2057 
2058 static char *meetme_lock_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2059 {
2060  switch (cmd) {
2061  case CLI_INIT:
2062  e->command = "meetme {lock|unlock}";
2063  e->usage =
2064  "Usage: meetme lock|unlock <confno>\n"
2065  " Lock or unlock a conference to new users.\n";
2066  return NULL;
2067  case CLI_GENERATE:
2068  return complete_meetmecmd_lock(a->word, a->pos, a->n);
2069  }
2070 
2071  if (a->argc != 3) {
2072  return CLI_SHOWUSAGE;
2073  }
2074 
2075  return meetme_cmd_helper(a);
2076 }
2077 
2078 static char *meetme_kick_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2079 {
2080  switch (cmd) {
2081  case CLI_INIT:
2082  e->command = "meetme kick";
2083  e->usage =
2084  "Usage: meetme kick <confno> all|<userno>\n"
2085  " Kick a conference or a user in a conference.\n";
2086  return NULL;
2087  case CLI_GENERATE:
2088  return complete_meetmecmd_mute_kick(a->line, a->word, a->pos, a->n);
2089  }
2090 
2091  if (a->argc != 4) {
2092  return CLI_SHOWUSAGE;
2093  }
2094 
2095  return meetme_cmd_helper(a);
2096 }
2097 
2098 static char *meetme_mute_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2099 {
2100  switch (cmd) {
2101  case CLI_INIT:
2102  e->command = "meetme {mute|unmute}";
2103  e->usage =
2104  "Usage: meetme mute|unmute <confno> all|<userno>\n"
2105  " Mute or unmute a conference or a user in a conference.\n";
2106  return NULL;
2107  case CLI_GENERATE:
2108  return complete_meetmecmd_mute_kick(a->line, a->word, a->pos, a->n);
2109  }
2110 
2111  if (a->argc != 4) {
2112  return CLI_SHOWUSAGE;
2113  }
2114 
2115  return meetme_cmd_helper(a);
2116 }
2117 
2118 static const char *sla_hold_str(unsigned int hold_access)
2119 {
2120  const char *hold = "Unknown";
2121 
2122  switch (hold_access) {
2123  case SLA_HOLD_OPEN:
2124  hold = "Open";
2125  break;
2126  case SLA_HOLD_PRIVATE:
2127  hold = "Private";
2128  default:
2129  break;
2130  }
2131 
2132  return hold;
2133 }
2134 
2135 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2136 {
2137  struct ao2_iterator i;
2138  struct sla_trunk *trunk;
2139 
2140  switch (cmd) {
2141  case CLI_INIT:
2142  e->command = "sla show trunks";
2143  e->usage =
2144  "Usage: sla show trunks\n"
2145  " This will list all trunks defined in sla.conf\n";
2146  return NULL;
2147  case CLI_GENERATE:
2148  return NULL;
2149  }
2150 
2151  ast_cli(a->fd, "\n"
2152  "=============================================================\n"
2153  "=== Configured SLA Trunks ===================================\n"
2154  "=============================================================\n"
2155  "===\n");
2156  i = ao2_iterator_init(sla_trunks, 0);
2157  for (; (trunk = ao2_iterator_next(&i)); ao2_ref(trunk, -1)) {
2158  struct sla_station_ref *station_ref;
2159  char ring_timeout[16] = "(none)";
2160 
2161  ao2_lock(trunk);
2162 
2163  if (trunk->ring_timeout) {
2164  snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
2165  }
2166 
2167  ast_cli(a->fd, "=== ---------------------------------------------------------\n"
2168  "=== Trunk Name: %s\n"
2169  "=== ==> Device: %s\n"
2170  "=== ==> AutoContext: %s\n"
2171  "=== ==> RingTimeout: %s\n"
2172  "=== ==> BargeAllowed: %s\n"
2173  "=== ==> HoldAccess: %s\n"
2174  "=== ==> Stations ...\n",
2175  trunk->name, trunk->device,
2176  S_OR(trunk->autocontext, "(none)"),
2177  ring_timeout,
2178  trunk->barge_disabled ? "No" : "Yes",
2179  sla_hold_str(trunk->hold_access));
2180 
2181  AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
2182  ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
2183  }
2184 
2185  ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
2186 
2187  ao2_unlock(trunk);
2188  }
2190  ast_cli(a->fd, "=============================================================\n\n");
2191 
2192  return CLI_SUCCESS;
2193 }
2194 
2195 static const char *trunkstate2str(enum sla_trunk_state state)
2196 {
2197 #define S(e) case e: return # e;
2198  switch (state) {
2204  }
2205  return "Uknown State";
2206 #undef S
2207 }
2208 
2209 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2210 {
2211  struct ao2_iterator i;
2212  struct sla_station *station;
2213 
2214  switch (cmd) {
2215  case CLI_INIT:
2216  e->command = "sla show stations";
2217  e->usage =
2218  "Usage: sla show stations\n"
2219  " This will list all stations defined in sla.conf\n";
2220  return NULL;
2221  case CLI_GENERATE:
2222  return NULL;
2223  }
2224 
2225  ast_cli(a->fd, "\n"
2226  "=============================================================\n"
2227  "=== Configured SLA Stations =================================\n"
2228  "=============================================================\n"
2229  "===\n");
2230  i = ao2_iterator_init(sla_stations, 0);
2231  for (; (station = ao2_iterator_next(&i)); ao2_ref(station, -1)) {
2232  struct sla_trunk_ref *trunk_ref;
2233  char ring_timeout[16] = "(none)";
2234  char ring_delay[16] = "(none)";
2235 
2236  ao2_lock(station);
2237 
2238  if (station->ring_timeout) {
2239  snprintf(ring_timeout, sizeof(ring_timeout),
2240  "%u", station->ring_timeout);
2241  }
2242  if (station->ring_delay) {
2243  snprintf(ring_delay, sizeof(ring_delay),
2244  "%u", station->ring_delay);
2245  }
2246  ast_cli(a->fd, "=== ---------------------------------------------------------\n"
2247  "=== Station Name: %s\n"
2248  "=== ==> Device: %s\n"
2249  "=== ==> AutoContext: %s\n"
2250  "=== ==> RingTimeout: %s\n"
2251  "=== ==> RingDelay: %s\n"
2252  "=== ==> HoldAccess: %s\n"
2253  "=== ==> Trunks ...\n",
2254  station->name, station->device,
2255  S_OR(station->autocontext, "(none)"),
2256  ring_timeout, ring_delay,
2257  sla_hold_str(station->hold_access));
2258  AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
2259  if (trunk_ref->ring_timeout) {
2260  snprintf(ring_timeout, sizeof(ring_timeout),
2261  "%u", trunk_ref->ring_timeout);
2262  } else {
2263  strcpy(ring_timeout, "(none)");
2264  }
2265  if (trunk_ref->ring_delay) {
2266  snprintf(ring_delay, sizeof(ring_delay),
2267  "%u", trunk_ref->ring_delay);
2268  } else {
2269  strcpy(ring_delay, "(none)");
2270  }
2271 
2272  ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
2273  "=== ==> State: %s\n"
2274  "=== ==> RingTimeout: %s\n"
2275  "=== ==> RingDelay: %s\n",
2276  trunk_ref->trunk->name,
2277  trunkstate2str(trunk_ref->state),
2278  ring_timeout, ring_delay);
2279  }
2280  ast_cli(a->fd, "=== ---------------------------------------------------------\n"
2281  "===\n");
2282 
2283  ao2_unlock(station);
2284  }
2286  ast_cli(a->fd, "============================================================\n"
2287  "\n");
2288 
2289  return CLI_SUCCESS;
2290 }
2291 
2292 static struct ast_cli_entry cli_meetme[] = {
2293  AST_CLI_DEFINE(meetme_kick_cmd, "Kick a conference or a user in a conference."),
2294  AST_CLI_DEFINE(meetme_show_cmd, "List all conferences or a specific conference."),
2295  AST_CLI_DEFINE(meetme_lock_cmd, "Lock or unlock a conference to new users."),
2296  AST_CLI_DEFINE(meetme_mute_cmd, "Mute or unmute a conference or a user in a conference."),
2297  AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
2298  AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
2299 };
2300 
2301 static void conf_flush(int fd, struct ast_channel *chan)
2302 {
2303  int x;
2304 
2305  /* read any frames that may be waiting on the channel
2306  and throw them away
2307  */
2308  if (chan) {
2309  struct ast_frame *f;
2310 
2311  /* when no frames are available, this will wait
2312  for 1 millisecond maximum
2313  */
2314  while (ast_waitfor(chan, 1) > 0) {
2315  f = ast_read(chan);
2316  if (f)
2317  ast_frfree(f);
2318  else /* channel was hung up or something else happened */
2319  break;
2320  }
2321  }
2322 
2323  /* flush any data sitting in the pseudo channel */
2324  x = DAHDI_FLUSH_ALL;
2325  if (ioctl(fd, DAHDI_FLUSH, &x))
2326  ast_log(LOG_WARNING, "Error flushing channel\n");
2327 
2328 }
2329 
2330 /*! \brief Remove the conference from the list and free it.
2331 
2332  We assume that this was called while holding conflock. */
2333 static int conf_free(struct ast_conference *conf)
2334 {
2335  int x;
2336  struct announce_listitem *item;
2337 
2338  AST_LIST_REMOVE(&confs, conf, list);
2339 
2340  meetme_stasis_generate_msg(conf, NULL, NULL, meetme_end_type(), NULL);
2341 
2342  if (conf->recording == MEETME_RECORD_ACTIVE) {
2345  while (1) {
2346  usleep(1);
2347  AST_LIST_LOCK(&confs);
2348  if (conf->recording == MEETME_RECORD_OFF)
2349  break;
2351  }
2352  }
2353 
2354  for (x = 0; x < AST_FRAME_BITS; x++) {
2355  if (conf->transframe[x])
2356  ast_frfree(conf->transframe[x]);
2357  if (conf->transpath[x])
2359  }
2360  if (conf->announcethread != AST_PTHREADT_NULL) {
2362  conf->announcethread_stop = 1;
2366  pthread_join(conf->announcethread, NULL);
2367 
2368  while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
2369  /* If it's a voicemail greeting file we don't want to remove it */
2370  if (!item->vmrec){
2371  ast_filedelete(item->namerecloc, NULL);
2372  }
2373  ao2_ref(item, -1);
2374  }
2376  }
2377 
2378  if (conf->origframe)
2379  ast_frfree(conf->origframe);
2380  ast_hangup(conf->lchan);
2381  ast_hangup(conf->chan);
2382  if (conf->fd >= 0)
2383  close(conf->fd);
2384  if (conf->recordingfilename) {
2385  ast_free(conf->recordingfilename);
2386  }
2387  if (conf->usercontainer) {
2388  ao2_ref(conf->usercontainer, -1);
2389  }
2390  if (conf->recordingformat) {
2391  ast_free(conf->recordingformat);
2392  }
2393  ast_mutex_destroy(&conf->playlock);
2394  ast_mutex_destroy(&conf->listenlock);
2397  ast_free(conf);
2398 
2399  return 0;
2400 }
2401 
2402 static void conf_queue_dtmf(const struct ast_conference *conf,
2403  const struct ast_conf_user *sender, struct ast_frame *f)
2404 {
2405  struct ast_conf_user *user;
2406  struct ao2_iterator user_iter;
2407 
2408  user_iter = ao2_iterator_init(conf->usercontainer, 0);
2409  while ((user = ao2_iterator_next(&user_iter))) {
2410  if (user == sender) {
2411  ao2_ref(user, -1);
2412  continue;
2413  }
2414  if (ast_write(user->chan, f) < 0)
2415  ast_log(LOG_WARNING, "Error writing frame to channel %s\n", ast_channel_name(user->chan));
2416  ao2_ref(user, -1);
2417  }
2418  ao2_iterator_destroy(&user_iter);
2419 }
2420 
2422  struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
2423 {
2424  struct sla_event *event;
2425 
2426  if (sla.thread == AST_PTHREADT_NULL) {
2427  ao2_ref(station, -1);
2428  ao2_ref(trunk_ref, -1);
2429  return;
2430  }
2431 
2432  if (!(event = ast_calloc(1, sizeof(*event)))) {
2433  ao2_ref(station, -1);
2434  ao2_ref(trunk_ref, -1);
2435  return;
2436  }
2437 
2438  event->type = type;
2439  event->trunk_ref = trunk_ref;
2440  event->station = station;
2441 
2442  if (!lock) {
2443  AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
2444  return;
2445  }
2446 
2447  ast_mutex_lock(&sla.lock);
2448  AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
2449  ast_cond_signal(&sla.cond);
2450  ast_mutex_unlock(&sla.lock);
2451 }
2452 
2454 {
2455  sla_queue_event_full(type, NULL, NULL, 0);
2456 }
2457 
2459 {
2460  sla_queue_event_full(type, NULL, NULL, 1);
2461 }
2462 
2463 /*! \brief Queue a SLA event from the conference */
2464 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
2465  struct ast_conference *conf)
2466 {
2467  struct sla_station *station;
2468  struct sla_trunk_ref *trunk_ref = NULL;
2469  char *trunk_name;
2470  struct ao2_iterator i;
2471 
2472  trunk_name = ast_strdupa(conf->confno);
2473  strsep(&trunk_name, "_");
2474  if (ast_strlen_zero(trunk_name)) {
2475  ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
2476  return;
2477  }
2478 
2479  i = ao2_iterator_init(sla_stations, 0);
2480  while ((station = ao2_iterator_next(&i))) {
2481  ao2_lock(station);
2482  AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
2483  if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name)) {
2484  ao2_ref(trunk_ref, 1);
2485  break;
2486  }
2487  }
2488  ao2_unlock(station);
2489  if (trunk_ref) {
2490  /* station reference given to sla_queue_event_full() */
2491  break;
2492  }
2493  ao2_ref(station, -1);
2494  }
2496 
2497  if (!trunk_ref) {
2498  ast_debug(1, "Trunk not found for event!\n");
2499  return;
2500  }
2501 
2502  sla_queue_event_full(type, trunk_ref, station, 1);
2503 }
2504 
2505 /*! \brief Decrement reference counts, as incremented by find_conf() */
2506 static int dispose_conf(struct ast_conference *conf)
2507 {
2508  int res = 0;
2509  int confno_int = 0;
2510 
2511  AST_LIST_LOCK(&confs);
2512  if (ast_atomic_dec_and_test(&conf->refcount)) {
2513  /* Take the conference room number out of an inuse state */
2514  if ((sscanf(conf->confno, "%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
2515  conf_map[confno_int] = 0;
2516  }
2517  conf_free(conf);
2518  res = 1;
2519  }
2521 
2522  return res;
2523 }
2524 
2525 static int rt_extend_conf(const char *confno)
2526 {
2527  char currenttime[32];
2528  char endtime[32];
2529  struct timeval now;
2530  struct ast_tm tm;
2531  struct ast_variable *var, *orig_var;
2532  char bookid[51];
2533 
2534  if (!extendby) {
2535  return 0;
2536  }
2537 
2538  now = ast_tvnow();
2539 
2540  ast_localtime(&now, &tm, NULL);
2541  ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2542 
2543  var = ast_load_realtime("meetme", "confno",
2544  confno, "startTime<= ", currenttime,
2545  "endtime>= ", currenttime, NULL);
2546 
2547  orig_var = var;
2548 
2549  /* Identify the specific RealTime conference */
2550  while (var) {
2551  if (!strcasecmp(var->name, "bookid")) {
2552  ast_copy_string(bookid, var->value, sizeof(bookid));
2553  }
2554  if (!strcasecmp(var->name, "endtime")) {
2555  ast_copy_string(endtime, var->value, sizeof(endtime));
2556  }
2557 
2558  var = var->next;
2559  }
2560  ast_variables_destroy(orig_var);
2561 
2562  ast_strptime(endtime, DATE_FORMAT, &tm);
2563  now = ast_mktime(&tm, NULL);
2564 
2565  now.tv_sec += extendby;
2566 
2567  ast_localtime(&now, &tm, NULL);
2568  ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2569  strcat(currenttime, "0"); /* Seconds needs to be 00 */
2570 
2571  var = ast_load_realtime("meetme", "confno",
2572  confno, "startTime<= ", currenttime,
2573  "endtime>= ", currenttime, NULL);
2574 
2575  /* If there is no conflict with extending the conference, update the DB */
2576  if (!var) {
2577  ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
2578  ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
2579  return 0;
2580 
2581  }
2582 
2583  ast_variables_destroy(var);
2584  return -1;
2585 }
2586 
2587 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
2588 {
2589  char *original_moh;
2590 
2591  ast_channel_lock(chan);
2592  original_moh = ast_strdupa(ast_channel_musicclass(chan));
2593  ast_channel_musicclass_set(chan, musicclass);
2594  ast_channel_unlock(chan);
2595 
2596  ast_moh_start(chan, original_moh, NULL);
2597 
2598  ast_channel_lock(chan);
2599  ast_channel_musicclass_set(chan, original_moh);
2600  ast_channel_unlock(chan);
2601 }
2602 
2603 static const char *get_announce_filename(enum announcetypes type)
2604 {
2605  switch (type) {
2606  case CONF_HASLEFT:
2607  return "conf-hasleft";
2608  break;
2609  case CONF_HASJOIN:
2610  return "conf-hasjoin";
2611  break;
2612  default:
2613  return "";
2614  }
2615 }
2616 
2617 static void *announce_thread(void *data)
2618 {
2619  struct announce_listitem *current;
2620  struct ast_conference *conf = data;
2621  int res;
2622  char filename[PATH_MAX] = "";
2624  AST_LIST_HEAD_INIT_NOLOCK(&local_list);
2625 
2626  while (!conf->announcethread_stop) {
2628  if (conf->announcethread_stop) {
2630  break;
2631  }
2632  if (AST_LIST_EMPTY(&conf->announcelist))
2634 
2635  AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
2637 
2639  if (conf->announcethread_stop) {
2640  break;
2641  }
2642 
2643  for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
2644  ast_debug(1, "About to play %s\n", current->namerecloc);
2645  if (!ast_fileexists(current->namerecloc, NULL, NULL))
2646  continue;
2647  if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
2648  if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
2649  res = ast_waitstream(current->confchan, "");
2650  if (!res) {
2651  ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
2652  if (!ast_streamfile(current->confchan, filename, current->language))
2653  ast_waitstream(current->confchan, "");
2654  }
2655  }
2656  if (current->announcetype == CONF_HASLEFT && current->announcetype && !current->vmrec) {
2657  /* only remove it if it isn't a VM recording file */
2658  ast_filedelete(current->namerecloc, NULL);
2659  }
2660  }
2661  }
2662 
2663  /* thread marked to stop, clean up */
2664  while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
2665  /* only delete if it's a vm rec */
2666  if (!current->vmrec) {
2667  ast_filedelete(current->namerecloc, NULL);
2668  }
2669  ao2_ref(current, -1);
2670  }
2671  return NULL;
2672 }
2673 
2674 static int can_write(struct ast_channel *chan, struct ast_flags64 *confflags)
2675 {
2676  if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
2677  return 1;
2678  }
2679 
2680  return (ast_channel_state(chan) == AST_STATE_UP);
2681 }
2682 
2683 static void send_talking_event(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking)
2684 {
2685  RAII_VAR(struct ast_json *, status_blob, status_to_json(talking), ast_json_unref);
2686  meetme_stasis_generate_msg(conf, chan, user, meetme_talking_type(), status_blob);
2687 }
2688 
2689 static void set_user_talking(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor)
2690 {
2691  int last_talking = user->talking;
2692  if (last_talking == talking)
2693  return;
2694 
2695  user->talking = talking;
2696 
2697  if (monitor) {
2698  /* Check if talking state changed. Take care of -1 which means unmonitored */
2699  int was_talking = (last_talking > 0);
2700  int now_talking = (talking > 0);
2701  if (was_talking != now_talking) {
2702  send_talking_event(chan, conf, user, now_talking);
2703  }
2704  }
2705 }
2706 
2707 static int user_set_hangup_cb(void *obj, void *check_admin_arg, int flags)
2708 {
2709  struct ast_conf_user *user = obj;
2710  /* actual pointer contents of check_admin_arg is irrelevant */
2711 
2712  if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
2713  user->adminflags |= ADMINFLAG_HANGUP;
2714  }
2715  return 0;
2716 }
2717 
2718 static int user_set_kickme_cb(void *obj, void *check_admin_arg, int flags)
2719 {
2720  struct ast_conf_user *user = obj;
2721  /* actual pointer contents of check_admin_arg is irrelevant */
2722 
2723  if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
2724  user->adminflags |= ADMINFLAG_KICKME;
2725  }
2726  return 0;
2727 }
2728 
2729 static int user_set_unmuted_cb(void *obj, void *check_admin_arg, int flags)
2730 {
2731  struct ast_conf_user *user = obj;
2732  /* actual pointer contents of check_admin_arg is irrelevant */
2733 
2734  if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
2736  }
2737  return 0;
2738 }
2739 
2740 static int user_set_muted_cb(void *obj, void *check_admin_arg, int flags)
2741 {
2742  struct ast_conf_user *user = obj;
2743  /* actual pointer contents of check_admin_arg is irrelevant */
2744 
2745  if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
2746  user->adminflags |= ADMINFLAG_MUTED;
2747  }
2748  return 0;
2749 }
2750 
2756 };
2757 
2758 /*! \internal
2759  * \brief Processes menu options for the standard menu (accessible through the 's' option for app_meetme)
2760  *
2761  * \param menu_mode a pointer to the currently active menu_mode.
2762  * \param dtmf a pointer to the dtmf value currently being processed against the menu.
2763  * \param conf the active conference for which the user has called the menu from.
2764  * \param confflags flags used by conf for various options
2765  * \param chan ast_channel belonging to the user who called the menu
2766  * \param user which meetme conference user invoked the menu
2767  */
2768 static void meetme_menu_normal(enum menu_modes *menu_mode, int *dtmf, struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, struct ast_conf_user *user)
2769 {
2770  switch (*dtmf) {
2771  case '1': /* Un/Mute */
2772  *menu_mode = MENU_DISABLED;
2773 
2774  /* user can only toggle the self-muted state */
2776 
2777  /* they can't override the admin mute state */
2779  if (!ast_streamfile(chan, "conf-muted", ast_channel_language(chan))) {
2780  ast_waitstream(chan, "");
2781  }
2782  } else {
2783  if (!ast_streamfile(chan, "conf-unmuted", ast_channel_language(chan))) {
2784  ast_waitstream(chan, "");
2785  }
2786  }
2787  break;
2788 
2789  case '2':
2790  *menu_mode = MENU_DISABLED;
2793  }
2794 
2795  if (user->adminflags & ADMINFLAG_T_REQUEST) {
2796  if (!ast_streamfile(chan, "beep", ast_channel_language(chan))) {
2797  ast_waitstream(chan, "");
2798  }
2799  }
2800  break;
2801 
2802  case '4':
2804  break;
2805  case '5':
2806  /* Extend RT conference */
2807  if (rt_schedule) {
2808  rt_extend_conf(conf->confno);
2809  }
2810  *menu_mode = MENU_DISABLED;
2811  break;
2812 
2813  case '6':
2814  tweak_listen_volume(user, VOL_UP);
2815  break;
2816 
2817  case '7':
2818  tweak_talk_volume(user, VOL_DOWN);
2819  break;
2820 
2821  case '8':
2822  *menu_mode = MENU_DISABLED;
2823  break;
2824 
2825  case '9':
2826  tweak_talk_volume(user, VOL_UP);
2827  break;
2828 
2829  default:
2830  *menu_mode = MENU_DISABLED;
2831  if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
2832  ast_waitstream(chan, "");
2833  }
2834  break;
2835  }
2836 }
2837 
2838 /*! \internal
2839  * \brief Processes menu options for the adminstrator menu (accessible through the 's' option for app_meetme)
2840  *
2841  * \param menu_mode a pointer to the currently active menu_mode.
2842  * \param dtmf a pointer to the dtmf value currently being processed against the menu.
2843  * \param conf the active conference for which the user has called the menu from.
2844  * \param confflags flags used by conf for various options
2845  * \param chan ast_channel belonging to the user who called the menu
2846  * \param user which meetme conference user invoked the menu
2847  */
2848 static void meetme_menu_admin(enum menu_modes *menu_mode, int *dtmf, struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, struct ast_conf_user *user)
2849 {
2850  switch(*dtmf) {
2851  case '1': /* Un/Mute */
2852  *menu_mode = MENU_DISABLED;
2853  /* for admin, change both admin and use flags */
2856  } else {
2858  }
2859 
2861  if (!ast_streamfile(chan, "conf-muted", ast_channel_language(chan))) {
2862  ast_waitstream(chan, "");
2863  }
2864  } else {
2865  if (!ast_streamfile(chan, "conf-unmuted", ast_channel_language(chan))) {
2866  ast_waitstream(chan, "");
2867  }
2868  }
2869  break;
2870 
2871  case '2': /* Un/Lock the Conference */
2872  *menu_mode = MENU_DISABLED;
2873  if (conf->locked) {
2874  conf->locked = 0;
2875  if (!ast_streamfile(chan, "conf-unlockednow", ast_channel_language(chan))) {
2876  ast_waitstream(chan, "");
2877  }
2878  } else {
2879  conf->locked = 1;
2880  if (!ast_streamfile(chan, "conf-lockednow", ast_channel_language(chan))) {
2881  ast_waitstream(chan, "");
2882  }
2883  }
2884  break;
2885 
2886  case '3': /* Eject last user */
2887  {
2888  struct ast_conf_user *usr = NULL;
2889  int max_no = 0;
2891  *menu_mode = MENU_DISABLED;
2892  usr = ao2_find(conf->usercontainer, &max_no, 0);
2894  if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
2895  ast_waitstream(chan, "");
2896  }
2897  } else {
2898  usr->adminflags |= ADMINFLAG_KICKME;
2899  }
2900  ao2_ref(usr, -1);
2901  ast_stopstream(chan);
2902  break;
2903  }
2904 
2905  case '4':
2907  break;
2908 
2909  case '5':
2910  /* Extend RT conference */
2911  if (rt_schedule) {
2912  if (!rt_extend_conf(conf->confno)) {
2913  if (!ast_streamfile(chan, "conf-extended", ast_channel_language(chan))) {
2914  ast_waitstream(chan, "");
2915  }
2916  } else {
2917  if (!ast_streamfile(chan, "conf-nonextended", ast_channel_language(chan))) {
2918  ast_waitstream(chan, "");
2919  }
2920  }
2921  ast_stopstream(chan);
2922  }
2923  *menu_mode = MENU_DISABLED;
2924  break;
2925 
2926  case '6':
2927  tweak_listen_volume(user, VOL_UP);
2928  break;
2929 
2930  case '7':
2931  tweak_talk_volume(user, VOL_DOWN);
2932  break;
2933 
2934  case '8':
2935  if (!ast_streamfile(chan, "conf-adminmenu-menu8", ast_channel_language(chan))) {
2936  /* If the user provides DTMF while playing the sound, we want to drop right into the extended menu function with new DTMF once we get out of here. */
2937  *dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2938  ast_stopstream(chan);
2939  }
2940  *menu_mode = MENU_ADMIN_EXTENDED;
2941  break;
2942 
2943  case '9':
2944  tweak_talk_volume(user, VOL_UP);
2945  break;
2946  default:
2947  *menu_mode = MENU_DISABLED;
2948  /* Play an error message! */
2949  if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
2950  ast_waitstream(chan, "");
2951  }
2952  break;
2953  }
2954 
2955 }
2956 
2957 /*! \internal
2958  * \brief Processes menu options for the extended administrator menu (accessible through option 8 on the administrator menu)
2959  *
2960  * \param menu_mode a pointer to the currently active menu_mode.
2961  * \param dtmf a pointer to the dtmf value currently being processed against the menu.
2962  * \param conf the active conference for which the user has called the menu from.
2963  * \param confflags flags used by conf for various options
2964  * \param chan ast_channel belonging to the user who called the menu
2965  * \param user which meetme conference user invoked the menu
2966  * \param recordingtmp character buffer which may hold the name of the conference recording file
2967  */
2968 static void meetme_menu_admin_extended(enum menu_modes *menu_mode, int *dtmf,
2969  struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan,
2970  struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size,
2971  struct ast_format_cap *cap_slin)
2972 {
2973  int keepplaying;
2974  int playednamerec;
2975  int res;
2976  struct ao2_iterator user_iter;
2977  struct ast_conf_user *usr = NULL;
2978 
2979  switch(*dtmf) {
2980  case '1': /* *81 Roll call */
2981  keepplaying = 1;
2982  playednamerec = 0;
2983  if (conf->users == 1) {
2984  if (keepplaying && !ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan))) {
2985  res = ast_waitstream(chan, AST_DIGIT_ANY);
2986  ast_stopstream(chan);
2987  if (res > 0) {
2988  keepplaying = 0;
2989  }
2990  }
2991  } else if (conf->users == 2) {
2992  if (keepplaying && !ast_streamfile(chan, "conf-onlyone", ast_channel_language(chan))) {
2993  res = ast_waitstream(chan, AST_DIGIT_ANY);
2994  ast_stopstream(chan);
2995  if (res > 0) {
2996  keepplaying = 0;
2997  }
2998  }
2999  } else {
3000  if (keepplaying && !ast_streamfile(chan, "conf-thereare", ast_channel_language(chan))) {
3001  res = ast_waitstream(chan, AST_DIGIT_ANY);
3002  ast_stopstream(chan);
3003  if (res > 0) {
3004  keepplaying = 0;
3005  }
3006  }
3007  if (keepplaying) {
3008  res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
3009  ast_stopstream(chan);
3010  if (res > 0) {
3011  keepplaying = 0;
3012  }
3013  }
3014  if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", ast_channel_language(chan))) {
3015  res = ast_waitstream(chan, AST_DIGIT_ANY);
3016  ast_stopstream(chan);
3017  if (res > 0) {
3018  keepplaying = 0;
3019  }
3020  }
3021  }
3022  user_iter = ao2_iterator_init(conf->usercontainer, 0);
3023  while((usr = ao2_iterator_next(&user_iter))) {
3024  if (ast_fileexists(usr->namerecloc, NULL, NULL)) {
3025  if (keepplaying && !ast_streamfile(chan, usr->namerecloc, ast_channel_language(chan))) {
3026  res = ast_waitstream(chan, AST_DIGIT_ANY);
3027  ast_stopstream(chan);
3028  if (res > 0) {
3029  keepplaying = 0;
3030  }
3031  }
3032  playednamerec = 1;
3033  }
3034  ao2_ref(usr, -1);
3035  }
3036  ao2_iterator_destroy(&user_iter);
3037  if (keepplaying && playednamerec && !ast_streamfile(chan, "conf-roll-callcomplete", ast_channel_language(chan))) {
3038  res = ast_waitstream(chan, AST_DIGIT_ANY);
3039  ast_stopstream(chan);
3040  if (res > 0) {
3041  keepplaying = 0;
3042  }
3043  }
3044 
3045  *menu_mode = MENU_DISABLED;
3046  break;
3047 
3048  case '2': /* *82 Eject all non-admins */
3049  if (conf->users == 1) {
3050  if(!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
3051  ast_waitstream(chan, "");
3052  }
3053  } else {
3055  }
3056  ast_stopstream(chan);
3057  *menu_mode = MENU_DISABLED;
3058  break;
3059 
3060  case '3': /* *83 (Admin) mute/unmute all non-admins */
3061  if(conf->gmuted) {
3062  conf->gmuted = 0;
3064  if (!ast_streamfile(chan, "conf-now-unmuted", ast_channel_language(chan))) {
3065  ast_waitstream(chan, "");
3066  }
3067  } else {
3068  conf->gmuted = 1;
3070  if (!ast_streamfile(chan, "conf-now-muted", ast_channel_language(chan))) {
3071  ast_waitstream(chan, "");
3072  }
3073  }
3074  ast_stopstream(chan);
3075  *menu_mode = MENU_DISABLED;
3076  break;
3077 
3078  case '4': /* *84 Record conference */
3079  if (conf->recording != MEETME_RECORD_ACTIVE) {
3080  ast_set_flag64(confflags, CONFFLAG_RECORDCONF);
3081  if (!conf->recordingfilename) {
3082  const char *var;
3083  ast_channel_lock(chan);
3084  if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
3085  conf->recordingfilename = ast_strdup(var);
3086  }
3087  if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
3088  conf->recordingformat = ast_strdup(var);
3089  }
3090  ast_channel_unlock(chan);
3091  if (!conf->recordingfilename) {
3092  snprintf(recordingtmp, recordingtmp_size, "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan));
3093  conf->recordingfilename = ast_strdup(recordingtmp);
3094  }
3095  if (!conf->recordingformat) {
3096  conf->recordingformat = ast_strdup("wav");
3097  }
3098  ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
3099  conf->confno, conf->recordingfilename, conf->recordingformat);
3100  }
3101 
3103  if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", cap_slin, NULL, chan, "pseudo", NULL)))) {
3104  struct dahdi_confinfo dahdic;
3105 
3108  dahdic.chan = 0;
3109  dahdic.confno = conf->dahdiconf;
3110  dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
3111  if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, &dahdic)) {
3112  ast_log(LOG_WARNING, "Error starting listen channel\n");
3113  ast_hangup(conf->lchan);
3114  conf->lchan = NULL;
3115  } else {
3116  ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
3117  }
3118  }
3120  if (!ast_streamfile(chan, "conf-now-recording", ast_channel_language(chan))) {
3121  ast_waitstream(chan, "");
3122  }
3123  }
3124 
3125  ast_stopstream(chan);
3126  *menu_mode = MENU_DISABLED;
3127  break;
3128 
3129  case '8': /* *88 Exit the menu and return to the conference... without an error message */
3130  ast_stopstream(chan);
3131  *menu_mode = MENU_DISABLED;
3132  break;
3133 
3134  default:
3135  if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
3136  ast_waitstream(chan, "");
3137  }
3138  ast_stopstream(chan);
3139  *menu_mode = MENU_DISABLED;
3140  break;
3141  }
3142 }
3143 
3144 /*! \internal
3145  * \brief Processes menu options for the various menu types (accessible through the 's' option for app_meetme)
3146  *
3147  * \param menu_mode a pointer to the currently active menu_mode.
3148  * \param dtmf a pointer to the dtmf value currently being processed against the menu.
3149  * \param conf the active conference for which the user has called the menu from.
3150  * \param confflags flags used by conf for various options
3151  * \param chan ast_channel belonging to the user who called the menu
3152  * \param user which meetme conference user invoked the menu
3153  * \param recordingtmp character buffer which may hold the name of the conference recording file
3154  */
3155 static void meetme_menu(enum menu_modes *menu_mode, int *dtmf,
3156  struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan,
3157  struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size,
3158  struct ast_format_cap *cap_slin)
3159 {
3160  switch (*menu_mode) {
3161  case MENU_DISABLED:
3162  break;
3163  case MENU_NORMAL:
3164  meetme_menu_normal(menu_mode, dtmf, conf, confflags, chan, user);
3165  break;
3166  case MENU_ADMIN:
3167  meetme_menu_admin(menu_mode, dtmf, conf, confflags, chan, user);
3168  /* Admin Menu is capable of branching into another menu, in which case it will reset dtmf and change the menu mode. */
3169  if (*menu_mode != MENU_ADMIN_EXTENDED || (*dtmf <= 0)) {
3170  break;
3171  }
3172  case MENU_ADMIN_EXTENDED:
3173  meetme_menu_admin_extended(menu_mode, dtmf, conf, confflags, chan, user,
3174  recordingtmp, recordingtmp_size, cap_slin);
3175  break;
3176  }
3177 }
3178 
3179 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struct ast_flags64 *confflags, char *optargs[])
3180 {
3181  struct ast_conf_user *user = NULL;
3182  int fd;
3183  struct dahdi_confinfo dahdic, dahdic_empty;
3184  struct ast_frame *f;
3185  struct ast_channel *c;
3186  struct ast_frame fr;
3187  int outfd;
3188  int ms;
3189  int nfds;
3190  int res;
3191  int retrydahdi;
3192  int origfd;
3193  int musiconhold = 0, mohtempstopped = 0;
3194  int firstpass = 0;
3195  int lastmarked = 0;
3196  int currentmarked = 0;
3197  int ret = -1;
3198  int x;
3199  enum menu_modes menu_mode = MENU_DISABLED;
3200  int talkreq_manager = 0;
3201  int using_pseudo = 0;
3202  int duration = 20;
3203  int sent_event = 0;
3204  int checked = 0;
3205  int announcement_played = 0;
3206  struct timeval now;
3207  struct ast_dsp *dsp = NULL;
3208  struct ast_app *agi_app;
3209  char *agifile;
3210  const char *agifiledefault = "conf-background.agi", *tmpvar;
3211  char meetmesecs[30] = "";
3212  char exitcontext[AST_MAX_CONTEXT] = "";
3213  char recordingtmp[AST_MAX_EXTENSION * 2] = "";
3214  char members[10] = "";
3215  int dtmf = 0, opt_waitmarked_timeout = 0;
3216  time_t timeout = 0;
3217  struct dahdi_bufferinfo bi;
3218  char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
3219  char *buf = __buf + AST_FRIENDLY_OFFSET;
3220  char *exitkeys = NULL;
3221  unsigned int calldurationlimit = 0;
3222  long timelimit = 0;
3223  long play_warning = 0;
3224  long warning_freq = 0;
3225  const char *warning_sound = NULL;
3226  const char *end_sound = NULL;
3227  char *parse;
3228  long time_left_ms = 0;
3229  struct timeval nexteventts = { 0, };
3230  int to;
3231  int setusercount = 0;
3232  int confsilence = 0, totalsilence = 0;
3233  char *mailbox, *context;
3235 
3236  if (!cap_slin) {
3237  goto conf_run_cleanup;
3238  }
3239  ast_format_cap_append(cap_slin, ast_format_slin, 0);
3240 
3241  if (!(user = ao2_alloc(sizeof(*user), NULL))) {
3242  goto conf_run_cleanup;
3243  }
3244 
3245  /* Possible timeout waiting for marked user */
3246  if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
3247  !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
3248  (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
3249  (opt_waitmarked_timeout > 0)) {
3250  timeout = time(NULL) + opt_waitmarked_timeout;
3251  }
3252 
3254  calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
3255  ast_verb(3, "Setting call duration limit to %u seconds.\n", calldurationlimit);
3256  }
3257 
3259  char *limit_str, *warning_str, *warnfreq_str;
3260  const char *var;
3261 
3262  parse = optargs[OPT_ARG_DURATION_LIMIT];
3263  limit_str = strsep(&parse, ":");
3264  warning_str = strsep(&parse, ":");
3265  warnfreq_str = parse;
3266 
3267  timelimit = atol(limit_str);
3268  if (warning_str)
3269  play_warning = atol(warning_str);
3270  if (warnfreq_str)
3271  warning_freq = atol(warnfreq_str);
3272 
3273  if (!timelimit) {
3274  timelimit = play_warning = warning_freq = 0;
3275  warning_sound = NULL;
3276  } else if (play_warning > timelimit) {
3277  if (!warning_freq) {
3278  play_warning = 0;
3279  } else {
3280  while (play_warning > timelimit)
3281  play_warning -= warning_freq;
3282  if (play_warning < 1)
3283  play_warning = warning_freq = 0;
3284  }
3285  }
3286 
3287  ast_verb(3, "Setting conference duration limit to: %ldms.\n", timelimit);
3288  if (play_warning) {
3289  ast_verb(3, "Setting warning time to %ldms from the conference duration limit.\n", play_warning);
3290  }
3291  if (warning_freq) {
3292  ast_verb(3, "Setting warning frequency to %ldms.\n", warning_freq);
3293  }
3294 
3295  ast_channel_lock(chan);
3296  if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
3297  var = ast_strdupa(var);
3298  }
3299  ast_channel_unlock(chan);
3300 
3301  warning_sound = var ? var : "timeleft";
3302 
3303  ast_channel_lock(chan);
3304  if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
3305  var = ast_strdupa(var);
3306  }
3307  ast_channel_unlock(chan);
3308 
3309  end_sound = var ? var : NULL;
3310 
3311  /* undo effect of S(x) in case they are both used */
3312  calldurationlimit = 0;
3313  /* more efficient do it like S(x) does since no advanced opts */
3314  if (!play_warning && !end_sound && timelimit) {
3315  calldurationlimit = timelimit / 1000;
3316  timelimit = play_warning = warning_freq = 0;
3317  } else {
3318  ast_debug(2, "Limit Data for this call:\n");
3319  ast_debug(2, "- timelimit = %ld\n", timelimit);
3320  ast_debug(2, "- play_warning = %ld\n", play_warning);
3321  ast_debug(2, "- warning_freq = %ld\n", warning_freq);
3322  ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
3323  ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
3324  }
3325  }
3326 
3327  /* Get exit keys */
3328  if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
3329  if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
3330  exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
3331  else
3332  exitkeys = ast_strdupa("#"); /* Default */
3333  }
3334 
3335  if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
3336  if (!conf->recordingfilename) {
3337  const char *var;
3338  ast_channel_lock(chan);
3339  if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
3340  conf->recordingfilename = ast_strdup(var);
3341  }
3342  if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
3343  conf->recordingformat = ast_strdup(var);
3344  }
3345  ast_channel_unlock(chan);
3346  if (!conf->recordingfilename) {
3347  snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan));
3348  conf->recordingfilename = ast_strdup(recordingtmp);
3349  }
3350  if (!conf->recordingformat) {
3351  conf->recordingformat = ast_strdup("wav");
3352  }
3353  ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
3354  conf->confno, conf->recordingfilename, conf->recordingformat);
3355  }
3356  }
3357 
3359  if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
3360  ((conf->lchan = ast_request("DAHDI", cap_slin, NULL, chan, "pseudo", NULL)))) {
3363  dahdic.chan = 0;
3364  dahdic.confno = conf->dahdiconf;
3365  dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
3366  if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, &dahdic)) {
3367  ast_log(LOG_WARNING, "Error starting listen channel\n");
3368  ast_hangup(conf->lchan);
3369  conf->lchan = NULL;
3370  } else {
3371  ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
3372  }
3373  }
3375 
3377  if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
3382  }
3384 
3385  time(&user->jointime);
3386 
3387  user->timelimit = timelimit;
3388  user->play_warning = play_warning;
3389  user->warning_freq = warning_freq;
3390  user->warning_sound = warning_sound;
3391  user->end_sound = end_sound;
3392 
3393  if (calldurationlimit > 0) {
3394  time(&user->kicktime);
3395  user->kicktime = user->kicktime + calldurationlimit;
3396  }
3397 
3398  if (ast_tvzero(user->start_time))
3399  user->start_time = ast_tvnow();
3400  time_left_ms = user->timelimit;
3401 
3402  if (user->timelimit) {
3403  nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
3404  nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
3405  }
3406 
3407  if (conf->locked && (!ast_test_flag64(confflags, CONFFLAG_ADMIN))) {
3408  /* Sorry, but this conference is locked! */
3409  if (!ast_streamfile(chan, "conf-locked", ast_channel_language(chan)))
3410  ast_waitstream(chan, "");
3411  goto outrun;
3412  }
3413 
3414  ast_mutex_lock(&conf->playlock);
3415 
3416  if (rt_schedule && conf->maxusers) {
3417  if (conf->users >= conf->maxusers) {
3418  /* Sorry, but this confernce has reached the participant limit! */
3419  ast_mutex_unlock(&conf->playlock);
3420  if (!ast_streamfile(chan, "conf-full", ast_channel_language(chan)))
3421  ast_waitstream(chan, "");
3422  goto outrun;
3423  }
3424  }
3425 
3426  ao2_lock(conf->usercontainer);
3428  user->user_no++;
3429  ao2_link(conf->usercontainer, user);
3430  ao2_unlock(conf->usercontainer);
3431 
3432  user->chan = chan;
3433  user->userflags = *confflags;
3435  if (!ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
3436  user->adminflags |= (conf->gmuted) ? ADMINFLAG_MUTED : 0;
3437  }
3438  user->talking = -1;
3439 
3440  ast_mutex_unlock(&conf->playlock);
3441 
3443  char destdir[PATH_MAX];
3444 
3445  snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
3446 
3447  if (ast_mkdir(destdir, 0777) != 0) {
3448  ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
3449  goto outrun;
3450  }
3451 
3452  if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
3453  context = ast_strdupa(optargs[OPT_ARG_INTROUSER_VMREC]);
3454  mailbox = strsep(&context, "@");
3455 
3456  if (ast_strlen_zero(mailbox)) {
3457  /* invalid input, clear the v flag*/
3459  ast_log(LOG_WARNING,"You must specify a mailbox in the v() option\n");
3460  } else {
3461  if (ast_strlen_zero(context)) {
3462  context = "default";
3463  }
3464  /* if there is no mailbox we don't need to do this logic */
3465  snprintf(user->namerecloc, sizeof(user->namerecloc),
3466  "%s/voicemail/%s/%s/greet",ast_config_AST_SPOOL_DIR,context,mailbox);
3467 
3468  /* if the greeting doesn't exist then use the temp file method instead, clear flag v */
3469  if (!ast_fileexists(user->namerecloc, NULL, NULL)){
3470  snprintf(user->namerecloc, sizeof(user->namerecloc),
3471  "%s/meetme-username-%s-%d", destdir,
3472  conf->confno, user->user_no);
3474  }
3475  }
3476  } else {
3477  snprintf(user->namerecloc, sizeof(user->namerecloc),
3478  "%s/meetme-username-%s-%d", destdir,
3479  conf->confno, user->user_no);
3480  }
3481 
3482  res = 0;
3484  res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
3485  else if (ast_test_flag64(confflags, CONFFLAG_INTROUSER) && !ast_fileexists(user->namerecloc, NULL, NULL))
3486  res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
3487  if (res == -1)
3488  goto outrun;
3489 
3490  }
3491 
3492  ast_mutex_lock(&conf->playlock);
3493 
3494  if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER))
3495  conf->markedusers++;
3496  conf->users++;
3497  if (rt_log_members) {
3498  /* Update table */
3499  snprintf(members, sizeof(members), "%d", conf->users);
3500  ast_realtime_require_field("meetme",
3501  "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
3502  "members", RQ_UINTEGER1, strlen(members),
3503  NULL);
3504  ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
3505  }
3506  setusercount = 1;
3507 
3508  /* This device changed state now - if this is the first user */
3509  if (conf->users == 1)
3511 
3512  ast_mutex_unlock(&conf->playlock);
3513 
3514  /* return the unique ID of the conference */
3515  pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
3516 
3517  if (ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT)) {
3518  ast_channel_lock(chan);
3519  if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
3520  ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
3521  } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))) {
3522  ast_copy_string(exitcontext, ast_channel_macrocontext(chan), sizeof(exitcontext));
3523  } else {
3524  ast_copy_string(exitcontext, ast_channel_context(chan), sizeof(exitcontext));
3525  }
3526  ast_channel_unlock(chan);
3527  }
3528 
3529  /* Play an arbitrary intro message */
3530  if (ast_test_flag64(confflags, CONFFLAG_INTROMSG) &&
3531  !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
3532  if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], ast_channel_language(chan))) {
3533  ast_waitstream(chan, "");
3534  }
3535  }
3536 
3537  if (!ast_test_flag64(confflags, (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
3538  if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED))
3539  if (!ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan)))
3540  ast_waitstream(chan, "");
3541  if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && conf->markedusers == 0)
3542  if (!ast_streamfile(chan, "conf-waitforleader", ast_channel_language(chan)))
3543  ast_waitstream(chan, "");
3544  }
3545 
3546  if (ast_test_flag64(confflags, CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
3547  int keepplaying = 1;
3548 
3549  if (conf->users == 2) {
3550  if (!ast_streamfile(chan, "conf-onlyone", ast_channel_language(chan))) {
3551  res = ast_waitstream(chan, AST_DIGIT_ANY);
3552  ast_stopstream(chan);
3553  if (res > 0)
3554  keepplaying = 0;
3555  else if (res == -1)
3556  goto outrun;
3557  }
3558  } else {
3559  if (!ast_streamfile(chan, "conf-thereare", ast_channel_language(chan))) {
3560  res = ast_waitstream(chan, AST_DIGIT_ANY);
3561  ast_stopstream(chan);
3562  if (res > 0)
3563  keepplaying = 0;
3564  else if (res == -1)
3565  goto outrun;
3566  }
3567  if (keepplaying) {
3568  res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
3569  if (res > 0)
3570  keepplaying = 0;
3571  else if (res == -1)
3572  goto outrun;
3573  }
3574  if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", ast_channel_language(chan))) {
3575  res = ast_waitstream(chan, AST_DIGIT_ANY);
3576  ast_stopstream(chan);
3577  if (res > 0)
3578  keepplaying = 0;
3579  else if (res == -1)
3580  goto outrun;
3581  }
3582  }
3583  }
3584 
3585  if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
3586  /* We're leaving this alone until the state gets changed to up */
3587  ast_indicate(chan, -1);
3588  }
3589 
3590  if (ast_set_write_format(chan, ast_format_slin) < 0) {
3591  ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", ast_channel_name(chan));
3592  goto outrun;
3593  }
3594 
3595  if (ast_set_read_format(chan, ast_format_slin) < 0) {
3596  ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", ast_channel_name(chan));
3597  goto outrun;
3598  }
3599 
3600  /* Reduce background noise from each participant */
3601  if (!ast_test_flag64(confflags, CONFFLAG_DONT_DENOISE)) {
3602  ast_func_write(chan, "DENOISE(rx)", "on");
3603  }
3604 
3605  retrydahdi = (strcasecmp(ast_channel_tech(chan)->type, "DAHDI") || (ast_channel_audiohooks(chan) || ast_channel_monitor(chan)) ? 1 : 0);
3606  user->dahdichannel = !retrydahdi;
3607 
3608  dahdiretry:
3609  origfd = ast_channel_fd(chan, 0);
3610  if (retrydahdi) {
3611  /* open pseudo in non-blocking mode */
3612  fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
3613  if (fd < 0) {
3614  ast_log(LOG_WARNING, "Unable to open DAHDI pseudo channel: %s\n", strerror(errno));
3615  goto outrun;
3616  }
3617  using_pseudo = 1;
3618  /* Setup buffering information */
3619  memset(&bi, 0, sizeof(bi));
3620  bi.bufsize = CONF_SIZE / 2;
3621  bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
3622  bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
3623  bi.numbufs = audio_buffers;
3624  if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
3625  ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
3626  close(fd);
3627  goto outrun;
3628  }
3629  x = 1;
3630  if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
3631  ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
3632  close(fd);
3633  goto outrun;
3634  }
3635  nfds = 1;
3636  } else {
3637  /* XXX Make sure we're not running on a pseudo channel XXX */
3638  fd = ast_channel_fd(chan, 0);
3639  nfds = 0;
3640  }
3641  memset(&dahdic, 0, sizeof(dahdic));
3642  memset(&dahdic_empty, 0, sizeof(dahdic_empty));
3643  /* Check to see if we're in a conference... */
3644  dahdic.chan = 0;
3645  if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
3646  ast_log(LOG_WARNING, "Error getting conference\n");
3647  close(fd);
3648  goto outrun;
3649  }
3650  if (dahdic.confmode) {
3651  /* Whoa, already in a conference... Retry... */
3652  if (!retrydahdi) {
3653  ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
3654  retrydahdi = 1;
3655  goto dahdiretry;
3656  }
3657  }
3658  memset(&dahdic, 0, sizeof(dahdic));
3659  /* Add us to the conference */
3660  dahdic.chan = 0;
3661  dahdic.confno = conf->dahdiconf;
3662 
3663  if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
3665  struct announce_listitem *item;
3666  if (!(item = ao2_alloc(sizeof(*item), NULL)))
3667  goto outrun;
3668  ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
3669  ast_copy_string(item->language, ast_channel_language(chan), sizeof(item->language));
3670  item->confchan = conf->chan;
3671  item->confusers = conf->users;
3672  if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
3673  item->vmrec = 1;
3674  }
3675  item->announcetype = CONF_HASJOIN;
3677  ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
3678  AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
3681 
3682  while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
3683  ;
3684  }
3685  ao2_ref(item, -1);
3686  }
3687 
3688  if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && !conf->markedusers)
3689  dahdic.confmode = DAHDI_CONF_CONF;
3690  else if (ast_test_flag64(confflags, CONFFLAG_MONITOR))
3691  dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
3692  else if (ast_test_flag64(confflags, CONFFLAG_TALKER))
3693  dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
3694  else
3695  dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
3696 
3697  if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3698  ast_log(LOG_WARNING, "Error setting conference\n");
3699  close(fd);
3700  goto outrun;
3701  }
3702  ast_debug(1, "Placed channel %s in DAHDI conf %d\n", ast_channel_name(chan), conf->dahdiconf);
3703 
3704  if (!sent_event) {
3705  meetme_stasis_generate_msg(conf, chan, user, meetme_join_type(), NULL);
3706  sent_event = 1;
3707  }
3708 
3709  if (!firstpass && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
3710  !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
3711  firstpass = 1;
3712  if (!ast_test_flag64(confflags, CONFFLAG_QUIET))
3713  if (!ast_test_flag64(confflags, CONFFLAG_WAITMARKED) || (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
3714  (conf->markedusers >= 1))) {
3715  conf_play(chan, conf, ENTER);
3716  }
3717  }
3718 
3719  conf_flush(fd, chan);
3720 
3721  if (dsp)
3722  ast_dsp_free(dsp);
3723 
3724  if (!(dsp = ast_dsp_new())) {
3725  ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
3726  res = -1;
3727  }
3728 
3729  if (ast_test_flag64(confflags, CONFFLAG_AGI)) {
3730  /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
3731  or use default filename of conf-background.agi */
3732 
3733  ast_channel_lock(chan);
3734  if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
3735  agifile = ast_strdupa(tmpvar);
3736  } else {
3737  agifile = ast_strdupa(agifiledefault);
3738  }
3739  ast_channel_unlock(chan);
3740 
3741  if (user->dahdichannel) {
3742  /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
3743  x = 1;
3744  ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
3745  }
3746  /* Find a pointer to the agi app and execute the script */
3747  agi_app = pbx_findapp("agi");
3748  if (agi_app) {
3749  ret = pbx_exec(chan, agi_app, agifile);
3750  } else {
3751  ast_log(LOG_WARNING, "Could not find application (agi)\n");
3752  ret = -2;
3753  }
3754  if (user->dahdichannel) {
3755  /* Remove CONFMUTE mode on DAHDI channel */
3756  x = 0;
3757  ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
3758  }
3759  } else {
3760  int lastusers = conf->users;
3761  if (user->dahdichannel && ast_test_flag64(confflags, CONFFLAG_STARMENU)) {
3762  /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
3763  x = 1;
3764  ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
3765  }
3766 
3767  for (;;) {
3768  int menu_was_active = 0;
3769 
3770  outfd = -1;
3771  ms = -1;
3772  now = ast_tvnow();
3773 
3774  if (rt_schedule && conf->endtime) {
3775  char currenttime[32];
3776  long localendtime = 0;
3777  int extended = 0;
3778  struct ast_tm tm;
3779  struct ast_variable *var, *origvar;
3780  struct timeval tmp;
3781 
3782  if (now.tv_sec % 60 == 0) {
3783  if (!checked) {
3784  ast_localtime(&now, &tm, NULL);
3785  ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
3786  var = origvar = ast_load_realtime("meetme", "confno",
3787  conf->confno, "starttime <=", currenttime,
3788  "endtime >=", currenttime, NULL);
3789 
3790  for ( ; var; var = var->next) {
3791  if (!strcasecmp(var->name, "endtime")) {
3792  struct ast_tm endtime_tm;
3793  ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
3794  tmp = ast_mktime(&endtime_tm, NULL);
3795  localendtime = tmp.tv_sec;
3796  }
3797  }
3798  ast_variables_destroy(origvar);
3799 
3800  /* A conference can be extended from the
3801  Admin/User menu or by an external source */
3802  if (localendtime > conf->endtime){
3803  conf->endtime = localendtime;
3804  extended = 1;
3805  }
3806 
3807  if (conf->endtime && (now.tv_sec >= conf->endtime)) {
3808  ast_verbose("Quitting time...\n");
3809  goto outrun;
3810  }
3811 
3812  if (!announcement_played && conf->endalert) {
3813  if (now.tv_sec + conf->endalert >= conf->endtime) {
3814  if (!ast_streamfile(chan, "conf-will-end-in", ast_channel_language(chan)))
3815  ast_waitstream(chan, "");
3816  ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", ast_channel_language(chan));
3817  if (!ast_streamfile(chan, "minutes", ast_channel_language(chan)))
3818  ast_waitstream(chan, "");
3819  if (musiconhold) {
3820  conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3821  }
3822  announcement_played = 1;
3823  }
3824  }
3825 
3826  if (extended) {
3827  announcement_played = 0;
3828  }
3829 
3830  checked = 1;
3831  }
3832  } else {
3833  checked = 0;
3834  }
3835  }
3836 
3837  if (user->kicktime && (user->kicktime <= now.tv_sec)) {
3838  if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
3839  ret = 0;
3840  } else {
3841  ret = -1;
3842  }
3843  break;
3844  }
3845 
3846  to = -1;
3847  if (user->timelimit) {
3848  int minutes = 0, seconds = 0, remain = 0;
3849 
3850  to = ast_tvdiff_ms(nexteventts, now);
3851  if (to < 0) {
3852  to = 0;
3853  }
3854  time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
3855  if (time_left_ms < to) {
3856  to = time_left_ms;
3857  }
3858 
3859  if (time_left_ms <= 0) {
3860  if (user->end_sound) {
3861  res = ast_streamfile(chan, user->end_sound, ast_channel_language(chan));
3862  res = ast_waitstream(chan, "");
3863  }
3864  if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
3865  ret = 0;
3866  } else {
3867  ret = -1;
3868  }
3869  break;
3870  }
3871 
3872  if (!to) {
3873  if (time_left_ms >= 5000) {
3874 
3875  remain = (time_left_ms + 500) / 1000;
3876  if (remain / 60 >= 1) {
3877  minutes = remain / 60;
3878  seconds = remain % 60;
3879  } else {
3880  seconds = remain;
3881  }
3882 
3883  /* force the time left to round up if appropriate */
3884  if (user->warning_sound && user->play_warning) {
3885  if (!strcmp(user->warning_sound, "timeleft")) {
3886 
3887  res = ast_streamfile(chan, "vm-youhave", ast_channel_language(chan));
3888  res = ast_waitstream(chan, "");
3889  if (minutes) {
3890  res = ast_say_number(chan, minutes, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
3891  res = ast_streamfile(chan, "queue-minutes", ast_channel_language(chan));
3892  res = ast_waitstream(chan, "");
3893  }
3894  if (seconds) {
3895  res = ast_say_number(chan, seconds, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
3896  res = ast_streamfile(chan, "queue-seconds", ast_channel_language(chan));
3897  res = ast_waitstream(chan, "");
3898  }
3899  } else {
3900  res = ast_streamfile(chan, user->warning_sound, ast_channel_language(chan));
3901  res = ast_waitstream(chan, "");
3902  }
3903  if (musiconhold) {
3904  conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3905  }
3906  }
3907  }
3908  if (user->warning_freq) {
3909  nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
3910  } else {
3911  nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
3912  }
3913  }
3914  }
3915 
3916  now = ast_tvnow();
3917  if (timeout && now.tv_sec >= timeout) {
3918  if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
3919  ret = 0;
3920  } else {
3921  ret = -1;
3922  }
3923  break;
3924  }
3925 
3926  /* if we have just exited from the menu, and the user had a channel-driver
3927  volume adjustment, restore it
3928  */
3929  if (!menu_mode && menu_was_active && user->listen.desired && !user->listen.actual) {
3930  set_talk_volume(user, user->listen.desired);
3931  }
3932 
3933  menu_was_active = menu_mode;
3934 
3935  currentmarked = conf->markedusers;
3936  if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
3937  ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
3938  ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
3939  lastmarked == 0) {
3940  if (currentmarked == 1 && conf->users > 1) {
3941  ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
3942  if (conf->users - 1 == 1) {
3943  if (!ast_streamfile(chan, "conf-userwilljoin", ast_channel_language(chan))) {
3944  ast_waitstream(chan, "");
3945  }
3946  } else {
3947  if (!ast_streamfile(chan, "conf-userswilljoin", ast_channel_language(chan))) {
3948  ast_waitstream(chan, "");
3949  }
3950  }
3951  }
3952  if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
3953  if (!ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan))) {
3954  ast_waitstream(chan, "");
3955  }
3956  }
3957  }
3958 
3959  /* Update the struct with the actual confflags */
3960  user->userflags = *confflags;
3961 
3962  if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
3963  if (currentmarked == 0) {
3964  if (lastmarked != 0) {
3965  if (!ast_test_flag64(confflags, CONFFLAG_QUIET)) {
3966  if (!ast_streamfile(chan, "conf-leaderhasleft", ast_channel_language(chan))) {
3967  ast_waitstream(chan, "");
3968  }
3969  }
3970  if (ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
3971  if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
3972  ret = 0;
3973  }
3974  break;
3975  } else {
3976  dahdic.confmode = DAHDI_CONF_CONF;
3977  if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3978  ast_log(LOG_WARNING, "Error setting conference\n");
3979  close(fd);
3980  goto outrun;
3981  }
3982  }
3983  }
3984  if (!musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
3985  conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3986  musiconhold = 1;
3987  }
3988  } else if (currentmarked >= 1 && lastmarked == 0) {
3989  /* Marked user entered, so cancel timeout */
3990  timeout = 0;
3991  if (ast_test_flag64(confflags, CONFFLAG_MONITOR)) {
3992  dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
3993  } else if (ast_test_flag64(confflags, CONFFLAG_TALKER)) {
3994  dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
3995  } else {
3996  dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
3997  }
3998  if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3999  ast_log(LOG_WARNING, "Error setting conference\n");
4000  close(fd);
4001  goto outrun;
4002  }
4003  if (musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
4004  ast_moh_stop(chan);
4005  musiconhold = 0;
4006  }
4007  if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
4008  !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
4009  if (!ast_streamfile(chan, "conf-placeintoconf", ast_channel_language(chan))) {
4010  ast_waitstream(chan, "");
4011  }
4012  conf_play(chan, conf, ENTER);
4013  }
4014  }
4015  }
4016 
4017  /* trying to add moh for single person conf */
4018  if (ast_test_flag64(confflags, CONFFLAG_MOH) && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
4019  if (conf->users == 1) {
4020  if (!musiconhold) {
4021  conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
4022  musiconhold = 1;
4023  }
4024  } else {
4025  if (musiconhold) {
4026  ast_moh_stop(chan);
4027  musiconhold = 0;
4028  }
4029  }
4030  }
4031 
4032  /* Leave if the last marked user left */
4033  if (currentmarked == 0 && lastmarked != 0 && ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
4034  if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
4035  ret = 0;
4036  } else {
4037  ret = -1;
4038  }
4039  break;
4040  }
4041 
4042  /* Throw a TestEvent if a user exit did not cause this user to leave the conference */
4043  if (conf->users != lastusers) {
4044  if (conf->users < lastusers) {
4045  ast_test_suite_event_notify("NOEXIT", "Message: CONFFLAG_MARKEDEXIT\r\nLastUsers: %d\r\nUsers: %d", lastusers, conf->users);
4046  }
4047  lastusers = conf->users;
4048  }
4049 
4050  /* Check if my modes have changed */
4051 
4052  /* If I should be muted but am still talker, mute me */
4053  if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
4054  RAII_VAR(struct ast_json *, status_blob, status_to_json(1), ast_json_unref);
4055  dahdic.confmode ^= DAHDI_CONF_TALKER;
4056  if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
4057  ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
4058  ret = -1;
4059  break;
4060  }
4061 
4062  /* Indicate user is not talking anymore - change him to unmonitored state */
4064  set_user_talking(chan, conf, user, -1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
4065  }
4066  meetme_stasis_generate_msg(conf, chan, user, meetme_mute_type(), status_blob);
4067  }
4068 
4069  /* If I should be un-muted but am not talker, un-mute me */
4070  if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
4071  RAII_VAR(struct ast_json *, status_blob, status_to_json(0), ast_json_unref);
4072  dahdic.confmode |= DAHDI_CONF_TALKER;
4073  if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
4074  ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
4075  ret = -1;
4076  break;
4077  }
4078  meetme_stasis_generate_msg(conf, chan, user, meetme_mute_type(), status_blob);
4079  }
4080 
4081  if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
4082  (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
4083 
4084  RAII_VAR(struct ast_json *, status_blob, status_to_json(1), ast_json_unref);
4085  talkreq_manager = 1;
4086  meetme_stasis_generate_msg(conf, chan, user, meetme_talk_request_type(), status_blob);
4087  }
4088 
4089  if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
4090  !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
4091  RAII_VAR(struct ast_json *, status_blob, status_to_json(0), ast_json_unref);
4092  talkreq_manager = 0;
4093  meetme_stasis_generate_msg(conf, chan, user, meetme_talk_request_type(), status_blob);
4094  }
4095 
4096  /* If user have been hung up, exit the conference */
4097  if (user->adminflags & ADMINFLAG_HANGUP) {
4098  ret = 0;
4099  break;
4100  }
4101 
4102  /* If I have been kicked, exit the conference */
4103  if (user->adminflags & ADMINFLAG_KICKME) {
4104  /* You have been kicked. */
4105  if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
4106  !ast_streamfile(chan, "conf-kicked", ast_channel_language(chan))) {
4107  ast_waitstream(chan, "");
4108  }
4109  ret = 0;
4110  break;
4111  }
4112 
4113  /* Perform a hangup check here since ast_waitfor_nandfds will not always be able to get a channel after a hangup has occurred */
4114  if (ast_check_hangup(chan)) {
4115  break;
4116  }
4117 
4118  c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
4119 
4120  if (c) {
4121  char dtmfstr[2] = "";
4122 
4123  if (ast_channel_fd(c, 0) != origfd || (user->dahdichannel && (ast_channel_audiohooks(c) || ast_channel_monitor(c)))) {
4124  if (using_pseudo) {
4125  /* Kill old pseudo */
4126  close(fd);
4127  using_pseudo = 0;
4128  }
4129  ast_debug(1, "Ooh, something swapped out under us, starting over\n");
4130  retrydahdi = (strcasecmp(ast_channel_tech(c)->type, "DAHDI") || (ast_channel_audiohooks(c) || ast_channel_monitor(c)) ? 1 : 0);
4131  user->dahdichannel = !retrydahdi;
4132  goto dahdiretry;
4133  }
4135  f = ast_read_noaudio(c);
4136  } else {
4137  f = ast_read(c);
4138  }
4139  if (!f) {
4140  break;
4141  }
4142  if (f->frametype == AST_FRAME_DTMF) {
4143  dtmfstr[0] = f->subclass.integer;
4144  dtmfstr[1] = '\0';
4145  }
4146 
4148  if (user->talk.actual) {
4150  }
4151 
4153  if (user->talking == -1) {
4154  user->talking = 0;
4155  }
4156 
4157  res = ast_dsp_silence(dsp, f, &totalsilence);
4158  if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
4159  set_user_talking(chan, conf, user, 1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
4160  }
4161 
4162  if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
4163  set_user_talking(chan, conf, user, 0, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
4164  }
4165  }
4166  if (using_pseudo) {
4167  /* Absolutely do _not_ use careful_write here...
4168  it is important that we read data from the channel
4169  as fast as it arrives, and feed it into the conference.
4170  The buffering in the pseudo channel will take care of any
4171  timing differences, unless they are so drastic as to lose
4172  audio frames (in which case carefully writing would only
4173  have delayed the audio even further).
4174  */
4175  /* As it turns out, we do want to use careful write. We just
4176  don't want to block, but we do want to at least *try*
4177  to write out all the samples.
4178  */
4179  if (user->talking || !ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER)) {
4180  careful_write(fd, f->data.ptr, f->datalen, 0);
4181  }
4182  }
4183  } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass.integer == '*') && ast_test_flag64(confflags, CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_mode)) {
4184  if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
4185  conf_queue_dtmf(conf, user, f);
4186  }
4187  /* Take out of conference */
4188  if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
4189  ast_log(LOG_WARNING, "Error setting conference\n");
4190  close(fd);
4191  ast_frfree(f);
4192  goto outrun;
4193  }
4194 
4195  /* if we are entering the menu, and the user has a channel-driver
4196  volume adjustment, clear it
4197  */
4198  if (!menu_mode && user->talk.desired && !user->talk.actual) {
4199  set_talk_volume(user, 0);
4200  }
4201 
4202  if (musiconhold) {
4203  ast_moh_stop(chan);
4204  } else if (!menu_mode) {
4205  char *menu_to_play;
4206  if (ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
4207  menu_mode = MENU_ADMIN;
4208  menu_to_play = "conf-adminmenu-18";
4209  } else {
4210  menu_mode = MENU_NORMAL;
4211  menu_to_play = "conf-usermenu-162";
4212  }
4213 
4214  if (!ast_streamfile(chan, menu_to_play, ast_channel_language(chan))) {
4215  dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
4216  ast_stopstream(chan);
4217  } else {
4218  dtmf = 0;
4219  }
4220  } else {
4221  dtmf = f->subclass.integer;
4222  }
4223 
4224  if (dtmf > 0) {
4225  meetme_menu(&menu_mode, &dtmf, conf, confflags,
4226  chan, user, recordingtmp, sizeof(recordingtmp), cap_slin);
4227  }
4228 
4229  if (musiconhold && !menu_mode) {
4230  conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
4231  }
4232 
4233  /* Put back into conference */
4234  if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
4235  ast_log(LOG_WARNING, "Error setting conference\n");
4236  close(fd);
4237  ast_frfree(f);
4238  goto outrun;
4239  }
4240 
4241  conf_flush(fd, chan);
4242  /*
4243  * Since options using DTMF could absorb DTMF meant for the
4244  * conference menu, we have to check them after the menu.
4245  */
4246  } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
4247  if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
4248  conf_queue_dtmf(conf, user, f);
4249  }
4250 
4251  if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
4252  ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
4253  ret = 0;
4254  ast_frfree(f);
4255  break;
4256  } else {
4257  ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
4258  }
4259  } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_KEYEXIT) &&
4260  (strchr(exitkeys, f->subclass.integer))) {
4261  pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);
4262 
4263  if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
4264  conf_queue_dtmf(conf, user, f);
4265  }
4266  ret = 0;
4267  ast_frfree(f);
4268  break;
4269  } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
4270  && ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
4271  conf_queue_dtmf(conf, user, f);
4272  } else if (ast_test_flag64(confflags, CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
4273  switch (f->subclass.integer) {
4274  case AST_CONTROL_HOLD:
4275  sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
4276  break;
4277  default:
4278  break;
4279  }
4280  } else if (f->frametype == AST_FRAME_NULL) {
4281  /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
4282  } else if (f->frametype == AST_FRAME_CONTROL) {
4283  switch (f->subclass.integer) {
4284  case AST_CONTROL_BUSY:
4286  ast_frfree(f);
4287  goto outrun;
4288  break;
4289  default:
4290  ast_debug(1,
4291  "Got ignored control frame on channel %s, f->frametype=%u,f->subclass=%d\n",
4292  ast_channel_name(chan), f->frametype, f->subclass.integer);
4293  }
4294  } else {
4295  ast_debug(1,
4296  "Got unrecognized frame on channel %s, f->frametype=%u,f->subclass=%d\n",
4297  ast_channel_name(chan), f->frametype, f->subclass.integer);
4298  }
4299  ast_frfree(f);
4300  } else if (outfd > -1) {
4301  res = read(outfd, buf, CONF_SIZE);
4302  if (res > 0) {
4303  memset(&fr, 0, sizeof(fr));
4306  fr.datalen = res;
4307  fr.samples = res / 2;
4308  fr.data.ptr = buf;
4310  if (!user->listen.actual &&
4311  (ast_test_flag64(confflags, CONFFLAG_MONITOR) ||
4313  (!user->talking && ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER))
4314  )) {
4315  int idx;
4316  for (idx = 0; idx < AST_FRAME_BITS; idx++) {
4318  break;
4319  }
4320  }
4321  if (idx >= AST_FRAME_BITS) {
4322  goto bailoutandtrynormal;
4323  }
4324  ast_mutex_lock(&conf->listenlock);
4325  if (!conf->transframe[idx]) {
4326  if (conf->origframe) {
4327  if (musiconhold
4328  && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)
4329  && !ast_dsp_silence(dsp, conf->origframe, &confsilence)
4330  && confsilence < MEETME_DELAYDETECTTALK) {
4331  ast_moh_stop(chan);
4332  mohtempstopped = 1;
4333  }
4334  if (!conf->transpath[idx]) {
4336  }
4337  if (conf->transpath[idx]) {
4338  conf->transframe[idx] = ast_translate(conf->transpath[idx], conf->origframe, 0);
4339  if (!conf->transframe[idx]) {
4340  conf->transframe[idx] = &ast_null_frame;
4341  }
4342  }
4343  }
4344  }
4345  if (conf->transframe[idx]) {
4346  if ((conf->transframe[idx]->frametype != AST_FRAME_NULL) &&
4347  can_write(chan, confflags)) {
4348  struct ast_frame *cur;
4349  /* the translator may have returned a list of frames, so
4350  write each one onto the channel
4351  */
4352  for (cur = conf->transframe[idx]; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
4353  if (ast_write(chan, cur)) {
4354  ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", ast_channel_name(chan));
4355  break;
4356  }
4357  }
4358  if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
4359  mohtempstopped = 0;
4360  conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
4361  }
4362  }
4363  } else {
4364  ast_mutex_unlock(&conf->listenlock);
4365  goto bailoutandtrynormal;
4366  }
4367  ast_mutex_unlock(&conf->listenlock);
4368  } else {
4369 bailoutandtrynormal:
4370  if (musiconhold
4371  && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)
4372  && !ast_dsp_silence(dsp, &fr, &confsilence)
4373  && confsilence < MEETME_DELAYDETECTTALK) {
4374  ast_moh_stop(chan);
4375  mohtempstopped = 1;
4376  }
4377  if (user->listen.actual) {
4379  }
4380  if (can_write(chan, confflags) && ast_write(chan, &fr) < 0) {
4381  ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", ast_channel_name(chan));
4382  }
4383  if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
4384  mohtempstopped = 0;
4385  conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
4386  }
4387  }
4388  } else {
4389  ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
4390  }
4391  }
4392  lastmarked = currentmarked;
4393  }
4394  }
4395 
4396  if (musiconhold) {
4397  ast_moh_stop(chan);
4398  }
4399 
4400  if (using_pseudo) {
4401  close(fd);
4402  } else {
4403  /* Take out of conference */
4404  dahdic.chan = 0;
4405  dahdic.confno = 0;
4406  dahdic.confmode = 0;
4407  if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
4408  ast_log(LOG_WARNING, "Error setting conference\n");
4409  }
4410  }
4411 
4412  reset_volumes(user);
4413 
4414  if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
4415  !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
4416  conf_play(chan, conf, LEAVE);
4417  }
4418 
4420  struct announce_listitem *item;
4421  if (!(item = ao2_alloc(sizeof(*item), NULL)))
4422  goto outrun;
4423  ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
4424  ast_copy_string(item->language, ast_channel_language(chan), sizeof(item->language));
4425  item->confchan = conf->chan;
4426  item->confusers = conf->users;
4427  item->announcetype = CONF_HASLEFT;
4428  if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
4429  item->vmrec = 1;
4430  }
4432  AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
4435  } else if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW) && !ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC) && conf->users == 1) {
4436  /* Last person is leaving, so no reason to try and announce, but should delete the name recording */
4437  ast_filedelete(user->namerecloc, NULL);
4438  }
4439 
4440  outrun:
4441  AST_LIST_LOCK(&confs);
4442 
4443  if (dsp) {
4444  ast_dsp_free(dsp);
4445  }
4446 
4447  if (user->user_no) {
4448  /* Only cleanup users who really joined! */
4449  now = ast_tvnow();
4450 
4451  if (sent_event) {
4452  meetme_stasis_generate_msg(conf, chan, user, meetme_leave_type(), NULL);
4453  }
4454 
4455  if (setusercount) {
4456  conf->users--;
4457  if (rt_log_members) {
4458  /* Update table */
4459  snprintf(members, sizeof(members), "%d", conf->users);
4460  ast_realtime_require_field("meetme",
4461  "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
4462  "members", RQ_UINTEGER1, strlen(members),
4463  NULL);
4464  ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
4465  }
4466  if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
4467  conf->markedusers--;
4468  }
4469  }
4470  /* Remove ourselves from the container */
4471  ao2_unlink(conf->usercontainer, user);
4472 
4473  /* Change any states */
4474  if (!conf->users) {
4475  ast_devstate_changed(AST_DEVICE_NOT_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);
4476  }
4477 
4478  /* This flag is meant to kill a conference with only one participant remaining. */
4479  if (conf->users == 1 && ast_test_flag64(confflags, CONFFLAG_KILL_LAST_MAN_STANDING)) {
4481  }
4482 
4483  /* Return the number of seconds the user was in the conf */
4484  snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
4485  pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
4486 
4487  /* Return the RealTime bookid for CDR linking */
4488  if (rt_schedule) {
4489  pbx_builtin_setvar_helper(chan, "MEETMEBOOKID", conf->bookid);
4490  }
4491  }
4492  ao2_ref(user, -1);
4494 
4495 
4496 conf_run_cleanup:
4497  ao2_cleanup(cap_slin);
4498 
4499  return ret;
4500 }
4501 
4502 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
4503  char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags64 *confflags, int *too_early, char **optargs)
4504 {
4505  struct ast_variable *var, *origvar;
4506  struct ast_conference *cnf;
4507 
4508  *too_early = 0;
4509 
4510  /* Check first in the conference list */
4511  AST_LIST_LOCK(&confs);
4512  AST_LIST_TRAVERSE(&confs, cnf, list) {
4513  if (!strcmp(confno, cnf->confno)) {
4514  break;
4515  }
4516  }
4517  if (cnf) {
4518  cnf->refcount += refcount;
4519  }
4521 
4522  if (!cnf) {
4523  char *pin = NULL, *pinadmin = NULL; /* For temp use */
4524  int maxusers = 0;
4525  struct timeval now;
4526  char recordingfilename[256] = "";
4527  char recordingformat[11] = "";
4528  char currenttime[32] = "";
4529  char eatime[32] = "";
4530  char bookid[51] = "";
4531  char recordingtmp[AST_MAX_EXTENSION * 2] = "";
4532  char useropts[OPTIONS_LEN + 1] = ""; /* Used for RealTime conferences */
4533  char adminopts[OPTIONS_LEN + 1] = "";
4534  struct ast_tm tm, etm;
4535  struct timeval endtime = { .tv_sec = 0 };
4536  const char *var2;
4537 
4538  if (rt_schedule) {
4539  now = ast_tvnow();
4540 
4541  ast_localtime(&now, &tm, NULL);
4542  ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
4543 
4544  ast_debug(1, "Looking for conference %s that starts after %s\n", confno, currenttime);
4545 
4546  var = ast_load_realtime("meetme", "confno",
4547  confno, "starttime <= ", currenttime, "endtime >= ",
4548  currenttime, NULL);
4549 
4550  if (!var && fuzzystart) {
4551  now = ast_tvnow();
4552  now.tv_sec += fuzzystart;
4553 
4554  ast_localtime(&now, &tm, NULL);
4555  ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
4556  var = ast_load_realtime("meetme", "confno",
4557  confno, "starttime <= ", currenttime, "endtime >= ",
4558  currenttime, NULL);
4559  }
4560 
4561  if (!var && earlyalert) {
4562  now = ast_tvnow();
4563  now.tv_sec += earlyalert;
4564  ast_localtime(&now, &etm, NULL);
4565  ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
4566  var = ast_load_realtime("meetme", "confno",
4567  confno, "starttime <= ", eatime, "endtime >= ",
4568  currenttime, NULL);
4569  if (var) {
4570  *too_early = 1;
4571  }
4572  }
4573 
4574  } else {
4575  var = ast_load_realtime("meetme", "confno", confno, NULL);
4576  }
4577 
4578  if (!var) {
4579  return NULL;
4580  }
4581 
4582  if (rt_schedule && *too_early) {
4583  /* Announce that the caller is early and exit */
4584  if (!ast_streamfile(chan, "conf-has-not-started", ast_channel_language(chan))) {
4585  ast_waitstream(chan, "");
4586  }
4587  ast_variables_destroy(var);
4588  return NULL;
4589  }
4590 
4591  for (origvar = var; var; var = var->next) {
4592  if (!strcasecmp(var->name, "pin")) {
4593  pin = ast_strdupa(var->value);
4594  } else if (!strcasecmp(var->name, "adminpin")) {
4595  pinadmin = ast_strdupa(var->value);
4596  } else if (!strcasecmp(var->name, "bookId")) {
4597  ast_copy_string(bookid, var->value, sizeof(bookid));
4598  } else if (!strcasecmp(var->name, "opts")) {
4599  ast_copy_string(useropts, var->value, sizeof(char[OPTIONS_LEN + 1]));
4600  } else if (!strcasecmp(var->name, "maxusers")) {
4601  maxusers = atoi(var->value);
4602  } else if (!strcasecmp(var->name, "adminopts")) {
4603  ast_copy_string(adminopts, var->value, sizeof(char[OPTIONS_LEN + 1]));
4604  } else if (!strcasecmp(var->name, "recordingfilename")) {
4605  ast_copy_string(recordingfilename, var->value, sizeof(recordingfilename));
4606  } else if (!strcasecmp(var->name, "recordingformat")) {
4607  ast_copy_string(recordingformat, var->value, sizeof(recordingformat));
4608  } else if (!strcasecmp(var->name, "endtime")) {
4609  struct ast_tm endtime_tm;
4610  ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
4611  endtime = ast_mktime(&endtime_tm, NULL);
4612  }
4613  }
4614 
4615  ast_variables_destroy(origvar);
4616 
4617  cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan, NULL);
4618 
4619  if (cnf) {
4620  struct ast_flags64 tmp_flags;
4621 
4622  cnf->maxusers = maxusers;
4623  cnf->endalert = endalert;
4624  cnf->endtime = endtime.tv_sec;
4625  cnf->useropts = ast_strdup(useropts);
4626  cnf->adminopts = ast_strdup(adminopts);
4627  cnf->bookid = ast_strdup(bookid);
4628  if (!ast_strlen_zero(recordingfilename)) {
4629  cnf->recordingfilename = ast_strdup(recordingfilename);
4630  }
4631  if (!ast_strlen_zero(recordingformat)) {
4632  cnf->recordingformat = ast_strdup(recordingformat);
4633  }
4634 
4635  /* Parse the other options into confflags -- need to do this in two
4636  * steps, because the parse_options routine zeroes the buffer. */
4637  ast_app_parse_options64(meetme_opts, &tmp_flags, optargs, useropts);
4638  ast_copy_flags64(confflags, &tmp_flags, tmp_flags.flags);
4639 
4640  if (strchr(cnf->useropts, 'r')) {
4641  if (ast_strlen_zero(recordingfilename)) { /* If the recordingfilename in the database is empty, use the channel definition or use the default. */
4642  ast_channel_lock(chan);
4643  if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
4645  cnf->recordingfilename = ast_strdup(var2);
4646  }
4647  ast_channel_unlock(chan);
4648  if (ast_strlen_zero(cnf->recordingfilename)) {
4649  snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", cnf->confno, ast_channel_uniqueid(chan));
4651  cnf->recordingfilename = ast_strdup(recordingtmp);
4652  }
4653  }
4654  if (ast_strlen_zero(cnf->recordingformat)) {/* If the recording format is empty, use the wav as default */
4655  ast_channel_lock(chan);
4656  if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
4657  ast_free(cnf->recordingformat);
4658  cnf->recordingformat = ast_strdup(var2);
4659  }
4660  ast_channel_unlock(chan);
4661  if (ast_strlen_zero(cnf->recordingformat)) {
4662  ast_free(cnf->recordingformat);
4663  cnf->recordingformat = ast_strdup("wav");
4664  }
4665  }
4666  ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
4667  }
4668  }
4669  }
4670 
4671  if (cnf) {
4672  if (confflags->flags && !cnf->chan &&
4673  !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
4675  ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
4677  }
4678 
4679  if (confflags && !cnf->chan &&
4680  ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
4681  ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
4683  }
4684  }
4685 
4686  return cnf;
4687 }
4688 
4689 
4690 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
4691  char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags64 *confflags)
4692 {
4693  struct ast_config *cfg;
4694  struct ast_variable *var;
4695  struct ast_flags config_flags = { 0 };
4696  struct ast_conference *cnf;
4697 
4698  AST_DECLARE_APP_ARGS(args,
4699  AST_APP_ARG(confno);
4700  AST_APP_ARG(pin);
4702  );
4703 
4704  /* Check first in the conference list */
4705  ast_debug(1, "The requested confno is '%s'?\n", confno);
4706  AST_LIST_LOCK(&confs);
4707  AST_LIST_TRAVERSE(&confs, cnf, list) {
4708  ast_debug(3, "Does conf %s match %s?\n", confno, cnf->confno);
4709  if (!strcmp(confno, cnf->confno))
4710  break;
4711  }
4712  if (cnf) {
4713  cnf->refcount += refcount;
4714  }
4716 
4717  if (!cnf) {
4718  if (dynamic) {
4719  /* No need to parse meetme.conf */
4720  ast_debug(1, "Building dynamic conference '%s'\n", confno);
4721  if (dynamic_pin) {
4722  if (dynamic_pin[0] == 'q') {
4723  /* Query the user to enter a PIN */
4724  if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
4725  return NULL;
4726  }
4727  cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan, NULL);
4728  } else {
4729  cnf = build_conf(confno, "", "", make, dynamic, refcount, chan, NULL);
4730  }
4731  } else {
4732  /* Check the config */
4733  cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
4734  if (!cfg) {
4735  ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
4736  return NULL;
4737  } else if (cfg == CONFIG_STATUS_FILEINVALID) {
4738  ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format. Aborting.\n");
4739  return NULL;
4740  }
4741 
4742  for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
4743  char parse[MAX_SETTINGS];
4744 
4745  if (strcasecmp(var->name, "conf"))
4746  continue;
4747 
4748  ast_copy_string(parse, var->value, sizeof(parse));
4749 
4750  AST_STANDARD_APP_ARGS(args, parse);
4751  ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
4752  if (!strcasecmp(args.confno, confno)) {
4753  /* Bingo it's a valid conference */
4754  cnf = build_conf(args.confno,
4755  S_OR(args.pin, ""),
4756  S_OR(args.pinadmin, ""),
4757  make, dynamic, refcount, chan, NULL);
4758  break;
4759  }
4760  }
4761  if (!var) {
4762  ast_debug(1, "%s isn't a valid conference\n", confno);
4763  }
4764  ast_config_destroy(cfg);
4765  }
4766  } else if (dynamic_pin) {
4767  /* Correct for the user selecting 'D' instead of 'd' to have
4768  someone join into a conference that has already been created
4769  with a pin. */
4770  if (dynamic_pin[0] == 'q') {
4771  dynamic_pin[0] = '\0';
4772  }
4773  }
4774 
4775  if (cnf) {
4776  if (confflags && !cnf->chan &&
4777  !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
4779  ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
4781  }
4782 
4783  if (confflags && !cnf->chan &&
4784  ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {