Asterisk - The Open Source Telephony Project  21.4.1
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  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*! \file
20  *
21  * \brief Meet me conference bridge
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * \ingroup applications
26  */
27 
28 /*! \li \ref app_meetme.c uses configuration file \ref meetme.conf
29  * \addtogroup configuration_file Configuration Files
30  */
31 
32 /*!
33  * \page meetme.conf meetme.conf
34  * \verbinclude meetme.conf.sample
35  */
36 
37 /*** MODULEINFO
38  <depend>dahdi</depend>
39  <defaultenabled>no</defaultenabled>
40  <support_level>deprecated</support_level>
41  <replacement>app_confbridge</replacement>
42  <deprecated_in>19</deprecated_in>
43  <removed_in>21</removed_in>
44  ***/
45 
46 #include "asterisk.h"
47 
48 #include <dahdi/user.h>
49 
50 #include "asterisk/lock.h"
51 #include "asterisk/file.h"
52 #include "asterisk/channel.h"
53 #include "asterisk/pbx.h"
54 #include "asterisk/module.h"
55 #include "asterisk/config.h"
56 #include "asterisk/app.h"
57 #include "asterisk/dsp.h"
58 #include "asterisk/musiconhold.h"
59 #include "asterisk/manager.h"
60 #include "asterisk/cli.h"
61 #include "asterisk/say.h"
62 #include "asterisk/utils.h"
63 #include "asterisk/translate.h"
64 #include "asterisk/ulaw.h"
65 #include "asterisk/astobj2.h"
66 #include "asterisk/devicestate.h"
67 #include "asterisk/dial.h"
68 #include "asterisk/causes.h"
69 #include "asterisk/paths.h"
70 #include "asterisk/test.h"
71 #include "asterisk/stasis.h"
72 #include "asterisk/stasis_channels.h"
73 #include "asterisk/stasis_message_router.h"
74 #include "asterisk/json.h"
76 
77 #include "enter.h"
78 #include "leave.h"
79 
80 /*** DOCUMENTATION
81  <application name="MeetMe" language="en_US">
82  <synopsis>
83  MeetMe conference bridge.
84  </synopsis>
85  <syntax>
86  <parameter name="confno">
87  <para>The conference number</para>
88  </parameter>
89  <parameter name="options">
90  <optionlist>
91  <option name="a">
92  <para>Set admin mode.</para>
93  </option>
94  <option name="A">
95  <para>Set marked mode.</para>
96  </option>
97  <option name="b">
98  <para>Run AGI script specified in <variable>MEETME_AGI_BACKGROUND</variable>
99  Default: <literal>conf-background.agi</literal>.</para>
100  <note><para>This does not work with non-DAHDI channels in the same
101  conference).</para></note>
102  </option>
103  <option name="c">
104  <para>Announce user(s) count on joining a conference.</para>
105  </option>
106  <option name="C">
107  <para>Continue in dialplan when kicked out of conference.</para>
108  </option>
109  <option name="d">
110  <para>Dynamically add conference.</para>
111  </option>
112  <option name="D">
113  <para>Dynamically add conference, prompting for a PIN.</para>
114  </option>
115  <option name="e">
116  <para>Select an empty conference.</para>
117  </option>
118  <option name="E">
119  <para>Select an empty pinless conference.</para>
120  </option>
121  <option name="F">
122  <para>Pass DTMF through the conference.</para>
123  </option>
124  <option name="G">
125  <argument name="x" required="true">
126  <para>The file to playback</para>
127  </argument>
128  <para>Play an intro announcement in conference.</para>
129  </option>
130  <option name="i">
131  <para>Announce user join/leave with review.</para>
132  </option>
133  <option name="I">
134  <para>Announce user join/leave without review.</para>
135  </option>
136  <option name="k">
137  <para>Close the conference if there's only one active participant left at exit.</para>
138  </option>
139  <option name="l">
140  <para>Set listen only mode (Listen only, no talking).</para>
141  </option>
142  <option name="m">
143  <para>Set initially muted.</para>
144  </option>
145  <option name="M" hasparams="optional">
146  <para>Enable music on hold when the conference has a single caller. Optionally,
147  specify a musiconhold class to use. If one is not provided, it will use the
148  channel's currently set music class, or <literal>default</literal>.</para>
149  <argument name="class" required="true" />
150  </option>
151  <option name="n">
152  <para>Disable the denoiser. By default, if <literal>func_speex</literal> is loaded, Asterisk
153  will apply a denoiser to channels in the MeetMe conference. However, channel
154  drivers that present audio with a varying rate will experience degraded
155  performance with a denoiser attached. This parameter allows a channel joining
156  the conference to choose not to have a denoiser attached without having to
157  unload <literal>func_speex</literal>.</para>
158  </option>
159  <option name="o">
160  <para>Set talker optimization - treats talkers who aren't speaking as
161  being muted, meaning (a) No encode is done on transmission and (b)
162  Received audio that is not registered as talking is omitted causing no
163  buildup in background noise.</para>
164  </option>
165  <option name="p" hasparams="optional">
166  <para>Allow user to exit the conference by pressing <literal>#</literal> (default)
167  or any of the defined keys. Dial plan execution will continue at the next
168  priority following MeetMe. The key used is set to channel variable
169  <variable>MEETME_EXIT_KEY</variable>.</para>
170  <argument name="keys" required="true" />
171  <note>
172  <para>Option <literal>s</literal> has priority for <literal>*</literal>
173  since it cannot change its activation code.</para>
174  </note>
175  </option>
176  <option name="P">
177  <para>Always prompt for the pin even if it is specified.</para>
178  </option>
179  <option name="q">
180  <para>Quiet mode (don't play enter/leave sounds).</para>
181  </option>
182  <option name="r">
183  <para>Record conference (records as <variable>MEETME_RECORDINGFILE</variable>
184  using format <variable>MEETME_RECORDINGFORMAT</variable>. Default filename is
185  <literal>meetme-conf-rec-${CONFNO}-${UNIQUEID}</literal> and the default format is
186  wav.</para>
187  </option>
188  <option name="s">
189  <para>Present menu (user or admin) when <literal>*</literal> is received
190  (send to menu).</para>
191  </option>
192  <option name="t">
193  <para>Set talk only mode. (Talk only, no listening).</para>
194  </option>
195  <option name="T">
196  <para>Set talker detection (sent to manager interface and meetme list).</para>
197  </option>
198  <option name="v" hasparams="optional">
199  <para>Announce when a user is joining or leaving the conference. Use the voicemail greeting as the announcement.
200  If the i or I options are set, the application will fall back to them if no voicemail greeting can be found.</para>
201  <argument name="mailbox@[context]" required="true">
202  <para>The mailbox and voicemail context to play from. If no context provided, assumed context is default.</para>
203  </argument>
204  </option>
205  <option name="w" hasparams="optional">
206  <para>Wait until the marked user enters the conference.</para>
207  <argument name="secs" required="true" />
208  </option>
209  <option name="x">
210  <para>Leave the conference when the last marked user leaves.</para>
211  </option>
212  <option name="X">
213  <para>Allow user to exit the conference by entering a valid single digit
214  extension <variable>MEETME_EXIT_CONTEXT</variable> or the current context
215  if that variable is not defined.</para>
216  <note>
217  <para>Option <literal>s</literal> has priority for <literal>*</literal>
218  since it cannot change its activation code.</para>
219  </note>
220  </option>
221  <option name="1">
222  <para>Do not play message when first person enters</para>
223  </option>
224  <option name="S">
225  <para>Kick the user <replaceable>x</replaceable> seconds <emphasis>after</emphasis> he entered into
226  the conference.</para>
227  <argument name="x" required="true" />
228  </option>
229  <option name="L" argsep=":">
230  <para>Limit the conference to <replaceable>x</replaceable> ms. Play a warning when
231  <replaceable>y</replaceable> ms are left. Repeat the warning every <replaceable>z</replaceable> ms.
232  The following special variables can be used with this option:</para>
233  <variablelist>
234  <variable name="CONF_LIMIT_TIMEOUT_FILE">
235  <para>File to play when time is up.</para>
236  </variable>
237  <variable name="CONF_LIMIT_WARNING_FILE">
238  <para>File to play as warning if <replaceable>y</replaceable> is defined. The
239  default is to say the time remaining.</para>
240  </variable>
241  </variablelist>
242  <argument name="x" />
243  <argument name="y" />
244  <argument name="z" />
245  </option>
246  </optionlist>
247  </parameter>
248  <parameter name="pin" />
249  </syntax>
250  <description>
251  <para>Enters the user into a specified MeetMe conference. If the <replaceable>confno</replaceable>
252  is omitted, the user will be prompted to enter one. User can exit the conference by hangup, or
253  if the <literal>p</literal> option is specified, by pressing <literal>#</literal>.</para>
254  <note><para>The DAHDI kernel modules and a functional DAHDI timing source (see dahdi_test)
255  must be present for conferencing to operate properly. In addition, the chan_dahdi channel driver
256  must be loaded for the <literal>i</literal> and <literal>r</literal> options to operate at
257  all.</para></note>
258  </description>
259  <see-also>
260  <ref type="application">MeetMeCount</ref>
261  <ref type="application">MeetMeAdmin</ref>
262  <ref type="application">MeetMeChannelAdmin</ref>
263  </see-also>
264  </application>
265  <application name="MeetMeCount" language="en_US">
266  <synopsis>
267  MeetMe participant count.
268  </synopsis>
269  <syntax>
270  <parameter name="confno" required="true">
271  <para>Conference number.</para>
272  </parameter>
273  <parameter name="var" />
274  </syntax>
275  <description>
276  <para>Plays back the number of users in the specified MeetMe conference.
277  If <replaceable>var</replaceable> is specified, playback will be skipped and the value
278  will be returned in the variable. Upon application completion, MeetMeCount will hangup
279  the channel, unless priority <literal>n+1</literal> exists, in which case priority progress will
280  continue.</para>
281  </description>
282  <see-also>
283  <ref type="application">MeetMe</ref>
284  </see-also>
285  </application>
286  <application name="MeetMeAdmin" language="en_US">
287  <synopsis>
288  MeetMe conference administration.
289  </synopsis>
290  <syntax>
291  <parameter name="confno" required="true" />
292  <parameter name="command" required="true">
293  <optionlist>
294  <option name="e">
295  <para>Eject last user that joined.</para>
296  </option>
297  <option name="E">
298  <para>Extend conference end time, if scheduled.</para>
299  </option>
300  <option name="k">
301  <para>Kick one user out of conference.</para>
302  </option>
303  <option name="K">
304  <para>Kick all users out of conference.</para>
305  </option>
306  <option name="l">
307  <para>Unlock conference.</para>
308  </option>
309  <option name="L">
310  <para>Lock conference.</para>
311  </option>
312  <option name="m">
313  <para>Unmute one user.</para>
314  </option>
315  <option name="M">
316  <para>Mute one user.</para>
317  </option>
318  <option name="n">
319  <para>Unmute all users in the conference.</para>
320  </option>
321  <option name="N">
322  <para>Mute all non-admin users in the conference.</para>
323  </option>
324  <option name="r">
325  <para>Reset one user's volume settings.</para>
326  </option>
327  <option name="R">
328  <para>Reset all users volume settings.</para>
329  </option>
330  <option name="s">
331  <para>Lower entire conference speaking volume.</para>
332  </option>
333  <option name="S">
334  <para>Raise entire conference speaking volume.</para>
335  </option>
336  <option name="t">
337  <para>Lower one user's talk volume.</para>
338  </option>
339  <option name="T">
340  <para>Raise one user's talk volume.</para>
341  </option>
342  <option name="u">
343  <para>Lower one user's listen volume.</para>
344  </option>
345  <option name="U">
346  <para>Raise one user's listen volume.</para>
347  </option>
348  <option name="v">
349  <para>Lower entire conference listening volume.</para>
350  </option>
351  <option name="V">
352  <para>Raise entire conference listening volume.</para>
353  </option>
354  </optionlist>
355  </parameter>
356  <parameter name="user" />
357  </syntax>
358  <description>
359  <para>Run admin <replaceable>command</replaceable> for conference <replaceable>confno</replaceable>.</para>
360  <para>Will additionally set the variable <variable>MEETMEADMINSTATUS</variable> with one of
361  the following values:</para>
362  <variablelist>
363  <variable name="MEETMEADMINSTATUS">
364  <value name="NOPARSE">
365  Invalid arguments.
366  </value>
367  <value name="NOTFOUND">
368  User specified was not found.
369  </value>
370  <value name="FAILED">
371  Another failure occurred.
372  </value>
373  <value name="OK">
374  The operation was completed successfully.
375  </value>
376  </variable>
377  </variablelist>
378  </description>
379  <see-also>
380  <ref type="application">MeetMe</ref>
381  </see-also>
382  </application>
383  <application name="MeetMeChannelAdmin" language="en_US">
384  <synopsis>
385  MeetMe conference Administration (channel specific).
386  </synopsis>
387  <syntax>
388  <parameter name="channel" required="true" />
389  <parameter name="command" required="true">
390  <optionlist>
391  <option name="k">
392  <para>Kick the specified user out of the conference he is in.</para>
393  </option>
394  <option name="m">
395  <para>Unmute the specified user.</para>
396  </option>
397  <option name="M">
398  <para>Mute the specified user.</para>
399  </option>
400  </optionlist>
401  </parameter>
402  </syntax>
403  <description>
404  <para>Run admin <replaceable>command</replaceable> for a specific
405  <replaceable>channel</replaceable> in any conference.</para>
406  </description>
407  </application>
408  <function name="MEETME_INFO" language="en_US">
409  <synopsis>
410  Query a given conference of various properties.
411  </synopsis>
412  <syntax>
413  <parameter name="keyword" required="true">
414  <para>Options:</para>
415  <enumlist>
416  <enum name="lock">
417  <para>Boolean of whether the corresponding conference is locked.</para>
418  </enum>
419  <enum name="parties">
420  <para>Number of parties in a given conference</para>
421  </enum>
422  <enum name="activity">
423  <para>Duration of conference in seconds.</para>
424  </enum>
425  <enum name="dynamic">
426  <para>Boolean of whether the corresponding conference is dynamic.</para>
427  </enum>
428  </enumlist>
429  </parameter>
430  <parameter name="confno" required="true">
431  <para>Conference number to retrieve information from.</para>
432  </parameter>
433  </syntax>
434  <description />
435  <see-also>
436  <ref type="application">MeetMe</ref>
437  <ref type="application">MeetMeCount</ref>
438  <ref type="application">MeetMeAdmin</ref>
439  <ref type="application">MeetMeChannelAdmin</ref>
440  </see-also>
441  </function>
442  <manager name="MeetmeMute" language="en_US">
443  <synopsis>
444  Mute a Meetme user.
445  </synopsis>
446  <syntax>
447  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
448  <parameter name="Meetme" required="true" />
449  <parameter name="Usernum" required="true" />
450  </syntax>
451  <description>
452  </description>
453  </manager>
454  <manager name="MeetmeUnmute" language="en_US">
455  <synopsis>
456  Unmute a Meetme user.
457  </synopsis>
458  <syntax>
459  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
460  <parameter name="Meetme" required="true" />
461  <parameter name="Usernum" required="true" />
462  </syntax>
463  <description>
464  </description>
465  </manager>
466  <manager name="MeetmeList" language="en_US">
467  <synopsis>
468  List participants in a conference.
469  </synopsis>
470  <syntax>
471  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
472  <parameter name="Conference" required="false">
473  <para>Conference number.</para>
474  </parameter>
475  </syntax>
476  <description>
477  <para>Lists all users in a particular MeetMe conference.
478  MeetmeList will follow as separate events, followed by a final event called
479  MeetmeListComplete.</para>
480  </description>
481  </manager>
482  <manager name="MeetmeListRooms" language="en_US">
483  <synopsis>
484  List active conferences.
485  </synopsis>
486  <syntax>
487  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
488  </syntax>
489  <description>
490  <para>Lists data about all active conferences.
491  MeetmeListRooms will follow as separate events, followed by a final event called
492  MeetmeListRoomsComplete.</para>
493  </description>
494  </manager>
495  <managerEvent language="en_US" name="MeetmeJoin">
496  <managerEventInstance class="EVENT_FLAG_CALL">
497  <synopsis>Raised when a user joins a MeetMe conference.</synopsis>
498  <syntax>
499  <parameter name="Meetme">
500  <para>The identifier for the MeetMe conference.</para>
501  </parameter>
502  <parameter name="User">
503  <para>The identifier of the MeetMe user who joined.</para>
504  </parameter>
505  <channel_snapshot/>
506  </syntax>
507  <see-also>
508  <ref type="managerEvent">MeetmeLeave</ref>
509  <ref type="application">MeetMe</ref>
510  </see-also>
511  </managerEventInstance>
512  </managerEvent>
513  <managerEvent language="en_US" name="MeetmeLeave">
514  <managerEventInstance class="EVENT_FLAG_CALL">
515  <synopsis>Raised when a user leaves a MeetMe conference.</synopsis>
516  <syntax>
517  <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter)" />
518  <channel_snapshot/>
519  <parameter name="Duration">
520  <para>The length of time in seconds that the Meetme user was in the conference.</para>
521  </parameter>
522  </syntax>
523  <see-also>
524  <ref type="managerEvent">MeetmeJoin</ref>
525  </see-also>
526  </managerEventInstance>
527  </managerEvent>
528  <managerEvent language="en_US" name="MeetmeEnd">
529  <managerEventInstance class="EVENT_FLAG_CALL">
530  <synopsis>Raised when a MeetMe conference ends.</synopsis>
531  <syntax>
532  <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
533  </syntax>
534  <see-also>
535  <ref type="managerEvent">MeetmeJoin</ref>
536  </see-also>
537  </managerEventInstance>
538  </managerEvent>
539  <managerEvent language="en_US" name="MeetmeTalkRequest">
540  <managerEventInstance class="EVENT_FLAG_CALL">
541  <synopsis>Raised when a MeetMe user has started talking.</synopsis>
542  <syntax>
543  <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter)" />
544  <channel_snapshot/>
545  <parameter name="Duration">
546  <para>The length of time in seconds that the Meetme user has been in the conference at the time of this event.</para>
547  </parameter>
548  <parameter name="Status">
549  <enumlist>
550  <enum name="on"/>
551  <enum name="off"/>
552  </enumlist>
553  </parameter>
554  </syntax>
555  </managerEventInstance>
556  </managerEvent>
557  <managerEvent language="en_US" name="MeetmeTalking">
558  <managerEventInstance class="EVENT_FLAG_CALL">
559  <synopsis>Raised when a MeetMe user begins or ends talking.</synopsis>
560  <syntax>
561  <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter)" />
562  <channel_snapshot/>
563  <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeTalkRequest']/managerEventInstance/syntax/parameter)" />
564  </syntax>
565  </managerEventInstance>
566  </managerEvent>
567  <managerEvent language="en_US" name="MeetmeMute">
568  <managerEventInstance class="EVENT_FLAG_CALL">
569  <synopsis>Raised when a MeetMe user is muted or unmuted.</synopsis>
570  <syntax>
571  <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter)" />
572  <channel_snapshot/>
573  <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeTalkRequest']/managerEventInstance/syntax/parameter)" />
574  </syntax>
575  </managerEventInstance>
576  </managerEvent>
577  <managerEvent language="en_US" name="MeetmeList">
578  <managerEventInstance class="EVENT_FLAG_CALL">
579  <synopsis>Raised in response to a MeetmeList command.</synopsis>
580  <syntax>
581  <parameter name="Conference">
582  <para>Conference ID.</para>
583  </parameter>
584  <parameter name="UserNumber">
585  <para>User ID.</para>
586  </parameter>
587  <parameter name="CallerIDNum">
588  <para>Caller ID number.</para>
589  </parameter>
590  <parameter name="CallerIDName">
591  <para>Caller ID name.</para>
592  </parameter>
593  <parameter name="ConnectedLineNum">
594  <para>Connected Line number.</para>
595  </parameter>
596  <parameter name="ConnectedLineName">
597  <para>Connected Line name.</para>
598  </parameter>
599  <parameter name="Channel">
600  <para>Channel name</para>
601  </parameter>
602  <parameter name="Admin">
603  <para>Whether or not the user is an admin.</para>
604  </parameter>
605  <parameter name="Role">
606  <para>User role. Can be "Listen only", "Talk only", or "Talk and listen".</para>
607  </parameter>
608  <parameter name="MarkedUser">
609  <para>Whether or not the user is a marked user.</para>
610  </parameter>
611  <parameter name="Muted">
612  <para>Whether or not the user is currently muted.</para>
613  </parameter>
614  <parameter name="Talking">
615  <para>Whether or not the user is currently talking.</para>
616  </parameter>
617  </syntax>
618  <see-also>
619  <ref type="manager">MeetmeList</ref>
620  <ref type="application">MeetMe</ref>
621  </see-also>
622  </managerEventInstance>
623  </managerEvent>
624  <managerEvent language="en_US" name="MeetmeListRooms">
625  <managerEventInstance class="EVENT_FLAG_CALL">
626  <synopsis>Raised in response to a MeetmeListRooms command.</synopsis>
627  <syntax>
628  <parameter name="Conference">
629  <para>Conference ID.</para>
630  </parameter>
631  <parameter name="Parties">
632  <para>Number of parties in the conference.</para>
633  </parameter>
634  <parameter name="Marked">
635  <para>Number of marked users in the conference.</para>
636  </parameter>
637  <parameter name="Activity">
638  <para>Total duration of conference in HH:MM:SS format.</para>
639  </parameter>
640  <parameter name="Creation">
641  <para>How the conference was created: "Dyanmic" or "Static".</para>
642  </parameter>
643  <parameter name="Locked">
644  <para>Whether or not the conference is locked.</para>
645  </parameter>
646  </syntax>
647  <see-also>
648  <ref type="manager">MeetmeListRooms</ref>
649  <ref type="application">MeetMe</ref>
650  </see-also>
651  </managerEventInstance>
652  </managerEvent>
653  ***/
654 
655 #define CONFIG_FILE_NAME "meetme.conf"
656 #define STR_CONCISE "concise"
657 
658 /*! each buffer is 20ms, so this is 640ms total */
659 #define DEFAULT_AUDIO_BUFFERS 32
660 
661 /*! String format for scheduled conferences */
662 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
663 
664 enum {
665  ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
666  ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
667  ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */
668  /*! User has requested to speak */
670  ADMINFLAG_HANGUP = (1 << 5), /*!< User will be leaving the conference */
671 };
672 
673 #define MEETME_DELAYDETECTTALK 300
674 #define MEETME_DELAYDETECTENDTALK 1000
675 
676 #define AST_FRAME_BITS 32
677 
678 enum volume_action {
679  VOL_UP,
680  VOL_DOWN
681 };
682 
683 enum entrance_sound {
684  ENTER,
685  LEAVE
686 };
687 
688 enum recording_state {
689  MEETME_RECORD_OFF,
690  MEETME_RECORD_STARTED,
691  MEETME_RECORD_ACTIVE,
692  MEETME_RECORD_TERMINATE
693 };
694 
695 #define CONF_SIZE 320
696 
697 enum {
698  /*! user has admin access on the conference */
699  CONFFLAG_ADMIN = (1 << 0),
700  /*! If set the user can only receive audio from the conference */
701  CONFFLAG_MONITOR = (1 << 1),
702  /*! If set asterisk will exit conference when key defined in p() option is pressed */
703  CONFFLAG_KEYEXIT = (1 << 2),
704  /*! If set asterisk will provide a menu to the user when '*' is pressed */
705  CONFFLAG_STARMENU = (1 << 3),
706  /*! If set the use can only send audio to the conference */
707  CONFFLAG_TALKER = (1 << 4),
708  /*! If set there will be no enter or leave sounds */
709  CONFFLAG_QUIET = (1 << 5),
710  /*! If set, when user joins the conference, they will be told the number
711  * of users that are already in */
713  /*! Set to run AGI Script in Background */
714  CONFFLAG_AGI = (1 << 7),
715  /*! Set to have music on hold when user is alone in conference */
716  CONFFLAG_MOH = (1 << 8),
717  /*! If set, the channel will leave the conference if all marked users leave */
719  /*! If set, the MeetMe will wait until a marked user enters */
720  CONFFLAG_WAITMARKED = (1 << 10),
721  /*! If set, the MeetMe will exit to the specified context */
723  /*! If set, the user will be marked */
724  CONFFLAG_MARKEDUSER = (1 << 12),
725  /*! If set, user will be ask record name on entry of conference */
726  CONFFLAG_INTROUSER = (1 << 13),
727  /*! If set, the MeetMe will be recorded */
729  /*! If set, the user will be monitored if the user is talking or not */
731  CONFFLAG_DYNAMIC = (1 << 16),
732  CONFFLAG_DYNAMICPIN = (1 << 17),
733  CONFFLAG_EMPTY = (1 << 18),
734  CONFFLAG_EMPTYNOPIN = (1 << 19),
735  CONFFLAG_ALWAYSPROMPT = (1 << 20),
736  /*! If set, treat talking users as muted users */
738  /*! If set, won't speak the extra prompt when the first person
739  * enters the conference */
741  /*! If set, user will be asked to record name on entry of conference
742  * without review */
744  /*! If set, the user will be initially self-muted */
745  CONFFLAG_STARTMUTED = (1 << 24),
746  /*! Pass DTMF through the conference */
747  CONFFLAG_PASS_DTMF = (1 << 25),
748  /*! If set, the user should continue in the dialplan if kicked out */
750  CONFFLAG_DURATION_STOP = (1 << 27),
751  CONFFLAG_DURATION_LIMIT = (1 << 28),
752 };
753 
754 /* These flags are defined separately because we ran out of bits that an enum can be used to represent.
755  If you add new flags, be sure to do it in the same way that these are. */
756 /*! Do not write any audio to this channel until the state is up. */
757 #define CONFFLAG_NO_AUDIO_UNTIL_UP (1ULL << 31)
758 #define CONFFLAG_INTROMSG (1ULL << 32) /*!< If set play an intro announcement at start of conference */
759 #define CONFFLAG_INTROUSER_VMREC (1ULL << 33)
760 /*! If there's only one person left in a conference when someone leaves, kill the conference */
761 #define CONFFLAG_KILL_LAST_MAN_STANDING (1ULL << 34)
762 /*! If set, don't enable a denoiser for the channel */
763 #define CONFFLAG_DONT_DENOISE (1ULL << 35)
764 
765 enum {
766  OPT_ARG_WAITMARKED = 0,
767  OPT_ARG_EXITKEYS = 1,
768  OPT_ARG_DURATION_STOP = 2,
769  OPT_ARG_DURATION_LIMIT = 3,
770  OPT_ARG_MOH_CLASS = 4,
771  OPT_ARG_INTROMSG = 5,
772  OPT_ARG_INTROUSER_VMREC = 6,
773  OPT_ARG_ARRAY_SIZE = 7,
774 };
775 
776 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
782  AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
783  AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
784  AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
785  AST_APP_OPTION('e', CONFFLAG_EMPTY ),
787  AST_APP_OPTION_ARG('G', CONFFLAG_INTROMSG, OPT_ARG_INTROMSG ),
788  AST_APP_OPTION_ARG('v', CONFFLAG_INTROUSER_VMREC , OPT_ARG_INTROUSER_VMREC),
792  AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
796  AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
797  AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
804  AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
808  AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
809  AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
810 END_OPTIONS );
811 
812 static const char * const app = "MeetMe";
813 static const char * const app2 = "MeetMeCount";
814 static const char * const app3 = "MeetMeAdmin";
815 static const char * const app4 = "MeetMeChannelAdmin";
816 
817 /* Lookup RealTime conferences based on confno and current time */
818 static int rt_schedule;
819 static int fuzzystart;
820 static int earlyalert;
821 static int endalert;
822 static int extendby;
823 
824 /*! Log participant count to the RealTime backend */
825 static int rt_log_members;
826 
827 #define MAX_CONFNUM 80
828 #define MAX_PIN 80
829 #define OPTIONS_LEN 100
830 
831 /* Enough space for "<conference #>,<pin>,<admin pin>" followed by a 0 byte. */
832 #define MAX_SETTINGS (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)
833 
834 enum announcetypes {
835  CONF_HASJOIN,
836  CONF_HASLEFT
837 };
838 
841  char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
842  char language[MAX_LANGUAGE];
843  struct ast_channel *confchan;
844  int confusers;
845  int vmrec;
846  enum announcetypes announcetype;
847 };
848 
849 /*! \brief The MeetMe Conference object */
851  ast_mutex_t playlock; /*!< Conference specific lock (players) */
852  ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
853  char confno[MAX_CONFNUM]; /*!< Conference */
854  struct ast_channel *chan; /*!< Announcements channel */
855  struct ast_channel *lchan; /*!< Listen/Record channel */
856  int fd; /*!< Announcements fd */
857  int dahdiconf; /*!< DAHDI Conf # */
858  int users; /*!< Number of active users */
859  int markedusers; /*!< Number of marked users */
860  int maxusers; /*!< Participant limit if scheduled */
861  int endalert; /*!< When to play conf ending message */
862  time_t start; /*!< Start time (s) */
863  int refcount; /*!< reference count of usage */
864  enum recording_state recording:2; /*!< recording status */
865  unsigned int isdynamic:1; /*!< Created on the fly? */
866  unsigned int locked:1; /*!< Is the conference locked? */
867  unsigned int gmuted:1; /*!< Is the conference globally muted? (all non-admins) */
868  pthread_t recordthread; /*!< thread for recording */
869  ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
870  pthread_attr_t attr; /*!< thread attribute */
871  char *recordingfilename; /*!< Filename to record the Conference into */
872  char *recordingformat; /*!< Format to record the Conference in */
873  char pin[MAX_PIN]; /*!< If protected by a PIN */
874  char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
875  char uniqueid[32];
876  long endtime; /*!< When to end the conf if scheduled */
877  const char *useropts; /*!< RealTime user flags */
878  const char *adminopts; /*!< RealTime moderator flags */
879  const char *bookid; /*!< RealTime conference id */
880  struct ast_frame *transframe[32];
881  struct ast_frame *origframe;
882  struct ast_trans_pvt *transpath[32];
883  struct ao2_container *usercontainer;
885  /* announce_thread related data */
886  pthread_t announcethread;
887  ast_mutex_t announcethreadlock;
888  unsigned int announcethread_stop:1;
889  ast_cond_t announcelist_addition;
890  AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
891  ast_mutex_t announcelistlock;
892 };
893 
895 
896 static unsigned int conf_map[1024] = {0, };
897 
898 struct volume {
899  int desired; /*!< Desired volume adjustment */
900  int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
901 };
902 
903 /*! \brief The MeetMe User object */
905  int user_no; /*!< User Number */
906  struct ast_flags64 userflags; /*!< Flags as set in the conference */
907  int adminflags; /*!< Flags set by the Admin */
908  struct ast_channel *chan; /*!< Connected channel */
909  int talking; /*!< Is user talking */
910  int dahdichannel; /*!< Is a DAHDI channel */
911  char usrvalue[50]; /*!< Custom User Value */
912  char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
913  time_t jointime; /*!< Time the user joined the conference */
914  time_t kicktime; /*!< Time the user will be kicked from the conference */
915  struct timeval start_time; /*!< Time the user entered into the conference */
916  long timelimit; /*!< Time limit for the user to be in the conference L(x:y:z) */
917  long play_warning; /*!< Play a warning when 'y' ms are left */
918  long warning_freq; /*!< Repeat the warning every 'z' ms */
919  const char *warning_sound; /*!< File to play as warning if 'y' is defined */
920  const char *end_sound; /*!< File to play when time is up. */
921  struct volume talk;
922  struct volume listen;
924 };
925 
926 /*! \brief The number of audio buffers to be allocated on pseudo channels
927  * when in a conference */
928 static int audio_buffers;
929 
930 /*! \brief Map 'volume' levels from -5 through +5 into decibel (dB)
931  * settings for channel drivers.
932  *
933  * \note these are not a straight linear-to-dB
934  * conversion... the numbers have been modified
935  * to give the user a better level of adjustability.
936  */
937 static const char gain_map[] = {
938  -15,
939  -13,
940  -10,
941  -6,
942  0,
943  0,
944  0,
945  6,
946  10,
947  13,
948  15,
949 };
950 
951 /* Routes the various meetme message types to the meetme stasis callback function to turn them into events */
952 static struct stasis_message_router *meetme_event_message_router;
953 
954 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_join_type);
955 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_leave_type);
956 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_end_type);
957 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_mute_type);
958 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_talking_type);
959 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_talk_request_type);
960 
961 static void meetme_stasis_cb(void *data, struct stasis_subscription *sub,
962  struct stasis_message *message);
963 
964 static void meetme_stasis_cleanup(void)
965 {
966  if (meetme_event_message_router) {
967  stasis_message_router_unsubscribe(meetme_event_message_router);
968  meetme_event_message_router = NULL;
969  }
970 
971  STASIS_MESSAGE_TYPE_CLEANUP(meetme_join_type);
972  STASIS_MESSAGE_TYPE_CLEANUP(meetme_leave_type);
973  STASIS_MESSAGE_TYPE_CLEANUP(meetme_end_type);
974  STASIS_MESSAGE_TYPE_CLEANUP(meetme_mute_type);
975  STASIS_MESSAGE_TYPE_CLEANUP(meetme_talking_type);
976  STASIS_MESSAGE_TYPE_CLEANUP(meetme_talk_request_type);
977 }
978 
979 static int meetme_stasis_init(void)
980 {
981 
982  STASIS_MESSAGE_TYPE_INIT(meetme_join_type);
983  STASIS_MESSAGE_TYPE_INIT(meetme_leave_type);
984  STASIS_MESSAGE_TYPE_INIT(meetme_end_type);
985  STASIS_MESSAGE_TYPE_INIT(meetme_mute_type);
986  STASIS_MESSAGE_TYPE_INIT(meetme_talking_type);
987  STASIS_MESSAGE_TYPE_INIT(meetme_talk_request_type);
988 
989  meetme_event_message_router = stasis_message_router_create(
991 
992  if (!meetme_event_message_router) {
993  meetme_stasis_cleanup();
994  return -1;
995  }
996 
997  if (stasis_message_router_add(meetme_event_message_router,
998  meetme_join_type(),
999  meetme_stasis_cb,
1000  NULL)) {
1001  meetme_stasis_cleanup();
1002  return -1;
1003  }
1004 
1005  if (stasis_message_router_add(meetme_event_message_router,
1006  meetme_leave_type(),
1007  meetme_stasis_cb,
1008  NULL)) {
1009  meetme_stasis_cleanup();
1010  return -1;
1011  }
1012 
1013  if (stasis_message_router_add(meetme_event_message_router,
1014  meetme_end_type(),
1015  meetme_stasis_cb,
1016  NULL)) {
1017  meetme_stasis_cleanup();
1018  return -1;
1019  }
1020 
1021  if (stasis_message_router_add(meetme_event_message_router,
1022  meetme_mute_type(),
1023  meetme_stasis_cb,
1024  NULL)) {
1025  meetme_stasis_cleanup();
1026  return -1;
1027  }
1028 
1029  if (stasis_message_router_add(meetme_event_message_router,
1030  meetme_talking_type(),
1031  meetme_stasis_cb,
1032  NULL)) {
1033  meetme_stasis_cleanup();
1034  return -1;
1035  }
1036 
1037  if (stasis_message_router_add(meetme_event_message_router,
1038  meetme_talk_request_type(),
1039  meetme_stasis_cb,
1040  NULL)) {
1041  meetme_stasis_cleanup();
1042  return -1;
1043  }
1044 
1045  return 0;
1046 }
1047 
1048 static void meetme_stasis_cb(void *data, struct stasis_subscription *sub,
1049  struct stasis_message *message)
1050 {
1051  struct ast_channel_blob *channel_blob = stasis_message_data(message);
1052  struct stasis_message_type *message_type;
1053  const char *event;
1054  const char *conference_num;
1055  const char *status;
1056  struct ast_json *json_cur;
1057  RAII_VAR(struct ast_str *, channel_text, NULL, ast_free);
1058  RAII_VAR(struct ast_str *, extra_text, NULL, ast_free);
1059 
1060  if (!channel_blob) {
1061  ast_assert(0);
1062  return;
1063  }
1064 
1065  message_type = stasis_message_type(message);
1066 
1067  if (!message_type) {
1068  ast_assert(0);
1069  return;
1070  }
1071 
1072  if (message_type == meetme_join_type()) {
1073  event = "MeetmeJoin";
1074  } else if (message_type == meetme_leave_type()) {
1075  event = "MeetmeLeave";
1076  } else if (message_type == meetme_end_type()) {
1077  event = "MeetmeEnd";
1078  } else if (message_type == meetme_mute_type()) {
1079  event = "MeetmeMute";
1080  } else if (message_type == meetme_talking_type()) {
1081  event = "MeetmeTalking";
1082  } else if (message_type == meetme_talk_request_type()) {
1083  event = "MeetmeTalkRequest";
1084  } else {
1085  ast_assert(0);
1086  return;
1087  }
1088 
1089  if (!event) {
1090  ast_assert(0);
1091  return;
1092  }
1093 
1094  conference_num = ast_json_string_get(ast_json_object_get(channel_blob->blob, "Meetme"));
1095  if (!conference_num) {
1096  ast_assert(0);
1097  return;
1098  }
1099 
1100  status = ast_json_string_get(ast_json_object_get(channel_blob->blob, "status"));
1101  if (status) {
1102  ast_str_append_event_header(&extra_text, "Status", status);
1103  }
1104 
1105  if (channel_blob->snapshot) {
1106  channel_text = ast_manager_build_channel_state_string(channel_blob->snapshot);
1107  }
1108 
1109  if ((json_cur = ast_json_object_get(channel_blob->blob, "user"))) {
1110  int user_number = ast_json_integer_get(json_cur);
1111  RAII_VAR(struct ast_str *, user_prop_str, ast_str_create(32), ast_free);
1112  if (!user_prop_str) {
1113  return;
1114  }
1115 
1116  ast_str_set(&user_prop_str, 0, "%d", user_number);
1117  ast_str_append_event_header(&extra_text, "User", ast_str_buffer(user_prop_str));
1118 
1119  if ((json_cur = ast_json_object_get(channel_blob->blob, "duration"))) {
1120  int duration = ast_json_integer_get(json_cur);
1121  ast_str_set(&user_prop_str, 0, "%d", duration);
1122  ast_str_append_event_header(&extra_text, "Duration", ast_str_buffer(user_prop_str));
1123  }
1124 
1125  json_cur = NULL;
1126  }
1127 
1128  manager_event(EVENT_FLAG_CALL, event,
1129  "Meetme: %s\r\n"
1130  "%s"
1131  "%s",
1132  conference_num,
1133  channel_text ? ast_str_buffer(channel_text) : "",
1134  extra_text ? ast_str_buffer(extra_text) : "");
1135 }
1136 
1137 /*!
1138  * \internal
1139  * \brief Build a json object from a status value for inclusion in json extras for meetme_stasis_generate_msg
1140  * \since 12.0.0
1141  *
1142  * \param on if true, then status is on. Otherwise status is off
1143  * \retval NULL on failure to allocate the JSON blob.
1144  * \retval pointer to the JSON blob if successful.
1145  */
1146 static struct ast_json *status_to_json(int on)
1147 {
1148  struct ast_json *json_object = ast_json_pack("{s: s}",
1149  "status", on ? "on" : "off");
1150 
1151  return json_object;
1152 }
1153 
1154 /*!
1155  * \internal
1156  * \brief Generate a stasis message associated with a meetme event
1157  * \since 12.0.0
1158  *
1159  * \param meetme_conference The conference responsible for generating this message
1160  * \param chan The channel involved in the message (NULL allowed)
1161  * \param user The conference user involved in the message (NULL allowed)
1162  * \param message_type the type the stasis message being generated
1163  * \param extras Additional json fields desired for inclusion
1164  */
1165 static void meetme_stasis_generate_msg(struct ast_conference *meetme_conference, struct ast_channel *chan,
1166  struct ast_conf_user *user, struct stasis_message_type *message_type, struct ast_json *extras)
1167 {
1168  RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
1169  RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
1170 
1171  json_object = ast_json_pack("{s: s}",
1172  "Meetme", meetme_conference->confno);
1173 
1174  if (!json_object) {
1175  return;
1176  }
1177 
1178  if (extras) {
1179  ast_json_object_update(json_object, extras);
1180  }
1181 
1182  if (user) {
1183  struct timeval now = ast_tvnow();
1184  long duration = (long)(now.tv_sec - user->jointime);
1185  struct ast_json *json_user;
1186  struct ast_json *json_user_duration;
1187 
1188  json_user = ast_json_integer_create(user->user_no);
1189  if (!json_user || ast_json_object_set(json_object, "user", json_user)) {
1190  return;
1191  }
1192 
1193  if (duration > 0) {
1194  json_user_duration = ast_json_integer_create(duration);
1195  if (!json_user_duration
1196  || ast_json_object_set(json_object, "duration", json_user_duration)) {
1197  return;
1198  }
1199  }
1200  }
1201 
1202  if (chan) {
1203  ast_channel_lock(chan);
1204  }
1205  msg = ast_channel_blob_create(chan, message_type, json_object);
1206  if (chan) {
1207  ast_channel_unlock(chan);
1208  }
1209 
1210  if (!msg) {
1211  return;
1212  }
1213 
1214  stasis_publish(ast_channel_topic(chan), msg);
1215 }
1216 
1217 static int admin_exec(struct ast_channel *chan, const char *data);
1218 static void *recordthread(void *args);
1219 
1220 static const char *istalking(int x)
1221 {
1222  if (x > 0)
1223  return "(talking)";
1224  else if (x < 0)
1225  return "(unmonitored)";
1226  else
1227  return "(not talking)";
1228 }
1229 
1230 static int careful_write(int fd, unsigned char *data, int len, int block)
1231 {
1232  int res;
1233  int x;
1234 
1235  while (len) {
1236  if (block) {
1237  x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
1238  res = ioctl(fd, DAHDI_IOMUX, &x);
1239  } else
1240  res = 0;
1241  if (res >= 0)
1242  res = write(fd, data, len);
1243  if (res < 1) {
1244  if (errno != EAGAIN) {
1245  ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
1246  return -1;
1247  } else
1248  return 0;
1249  }
1250  len -= res;
1251  data += res;
1252  }
1253 
1254  return 0;
1255 }
1256 
1257 static int set_talk_volume(struct ast_conf_user *user, int volume)
1258 {
1259  char gain_adjust;
1260 
1261  /* attempt to make the adjustment in the channel driver;
1262  if successful, don't adjust in the frame reading routine
1263  */
1264  gain_adjust = gain_map[volume + 5];
1265 
1266  return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1267 }
1268 
1269 static int set_listen_volume(struct ast_conf_user *user, int volume)
1270 {
1271  char gain_adjust;
1272 
1273  /* attempt to make the adjustment in the channel driver;
1274  if successful, don't adjust in the frame reading routine
1275  */
1276  gain_adjust = gain_map[volume + 5];
1277 
1278  return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1279 }
1280 
1281 static void tweak_volume(struct volume *vol, enum volume_action action)
1282 {
1283  switch (action) {
1284  case VOL_UP:
1285  switch (vol->desired) {
1286  case 5:
1287  break;
1288  case 0:
1289  vol->desired = 2;
1290  break;
1291  case -2:
1292  vol->desired = 0;
1293  break;
1294  default:
1295  vol->desired++;
1296  break;
1297  }
1298  break;
1299  case VOL_DOWN:
1300  switch (vol->desired) {
1301  case -5:
1302  break;
1303  case 2:
1304  vol->desired = 0;
1305  break;
1306  case 0:
1307  vol->desired = -2;
1308  break;
1309  default:
1310  vol->desired--;
1311  break;
1312  }
1313  }
1314 }
1315 
1316 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
1317 {
1318  tweak_volume(&user->talk, action);
1319  /* attempt to make the adjustment in the channel driver;
1320  if successful, don't adjust in the frame reading routine
1321  */
1322  if (!set_talk_volume(user, user->talk.desired))
1323  user->talk.actual = 0;
1324  else
1325  user->talk.actual = user->talk.desired;
1326 }
1327 
1328 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
1329 {
1330  tweak_volume(&user->listen, action);
1331  /* attempt to make the adjustment in the channel driver;
1332  if successful, don't adjust in the frame reading routine
1333  */
1334  if (!set_listen_volume(user, user->listen.desired))
1335  user->listen.actual = 0;
1336  else
1337  user->listen.actual = user->listen.desired;
1338 }
1339 
1340 static void reset_volumes(struct ast_conf_user *user)
1341 {
1342  signed char zero_volume = 0;
1343 
1344  ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
1345  ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
1346 }
1347 
1348 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
1349 {
1350  unsigned char *data;
1351  int len;
1352  int res = -1;
1353 
1354  ast_test_suite_event_notify("CONFPLAY", "Channel: %s\r\n"
1355  "Conference: %s\r\n"
1356  "Marked: %d",
1357  ast_channel_name(chan),
1358  conf->confno,
1359  conf->markedusers);
1360 
1361  if (!ast_check_hangup(chan))
1362  res = ast_autoservice_start(chan);
1363 
1364  AST_LIST_LOCK(&confs);
1365 
1366  switch(sound) {
1367  case ENTER:
1368  data = enter;
1369  len = sizeof(enter);
1370  break;
1371  case LEAVE:
1372  data = leave;
1373  len = sizeof(leave);
1374  break;
1375  default:
1376  data = NULL;
1377  len = 0;
1378  }
1379  if (data) {
1380  careful_write(conf->fd, data, len, 1);
1381  }
1382 
1384 
1385  if (!res)
1386  ast_autoservice_stop(chan);
1387 }
1388 
1389 static int user_no_cmp(void *obj, void *arg, int flags)
1390 {
1391  struct ast_conf_user *user = obj;
1392  int *user_no = arg;
1393 
1394  if (user->user_no == *user_no) {
1395  return (CMP_MATCH | CMP_STOP);
1396  }
1397 
1398  return 0;
1399 }
1400 
1401 static int user_max_cmp(void *obj, void *arg, int flags)
1402 {
1403  struct ast_conf_user *user = obj;
1404  int *max_no = arg;
1405 
1406  if (user->user_no > *max_no) {
1407  *max_no = user->user_no;
1408  }
1409 
1410  return 0;
1411 }
1412 
1413 /*!
1414  * \brief Find or create a conference
1415  *
1416  * \param confno The conference name/number
1417  * \param pin The regular user pin
1418  * \param pinadmin The admin pin
1419  * \param make Make the conf if it doesn't exist
1420  * \param dynamic Mark the newly created conference as dynamic
1421  * \param refcount How many references to mark on the conference
1422  * \param chan The asterisk channel
1423  * \param test
1424  *
1425  * \return A pointer to the conference struct, or NULL if it wasn't found and
1426  * make or dynamic were not set.
1427  */
1428 static struct ast_conference *build_conf(const char *confno, const char *pin,
1429  const char *pinadmin, int make, int dynamic, int refcount,
1430  const struct ast_channel *chan, struct ast_test *test)
1431 {
1432  struct ast_conference *cnf;
1433  struct dahdi_confinfo dahdic = { 0, };
1434  int confno_int = 0;
1436 
1437  AST_LIST_LOCK(&confs);
1438 
1439  AST_LIST_TRAVERSE(&confs, cnf, list) {
1440  if (!strcmp(confno, cnf->confno))
1441  break;
1442  }
1443 
1444  if (cnf || (!make && !dynamic) || !cap_slin)
1445  goto cnfout;
1446 
1447  ast_format_cap_append(cap_slin, ast_format_slin, 0);
1448  /* Make a new one */
1449  cnf = ast_calloc(1, sizeof(*cnf));
1450  if (!cnf) {
1451  goto cnfout;
1452  }
1453 
1454  cnf->usercontainer = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
1455  NULL, user_no_cmp);
1456  if (!cnf->usercontainer) {
1457  goto cnfout;
1458  }
1459 
1460  ast_mutex_init(&cnf->playlock);
1461  ast_mutex_init(&cnf->listenlock);
1462  cnf->recordthread = AST_PTHREADT_NULL;
1463  ast_mutex_init(&cnf->recordthreadlock);
1464  cnf->announcethread = AST_PTHREADT_NULL;
1465  ast_mutex_init(&cnf->announcethreadlock);
1466  ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
1467  ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
1468  ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
1469  ast_copy_string(cnf->uniqueid, ast_channel_uniqueid(chan), sizeof(cnf->uniqueid));
1470 
1471  /* Setup a new dahdi conference */
1472  dahdic.confno = -1;
1473  dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1474  cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
1475  if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
1476  if (test) {
1477  /* if we are creating a conference for a unit test, it is not neccesary
1478  * to open a pseudo channel, so, if we fail continue creating
1479  * the conference. */
1480  ast_test_status_update(test, "Unable to open DAHDI pseudo device\n");
1481  } else {
1482  ast_log(LOG_WARNING, "Unable to open DAHDI pseudo device\n");
1483  if (cnf->fd >= 0)
1484  close(cnf->fd);
1485  ao2_ref(cnf->usercontainer, -1);
1486  ast_mutex_destroy(&cnf->playlock);
1487  ast_mutex_destroy(&cnf->listenlock);
1488  ast_mutex_destroy(&cnf->recordthreadlock);
1489  ast_mutex_destroy(&cnf->announcethreadlock);
1490  ast_free(cnf);
1491  cnf = NULL;
1492  goto cnfout;
1493  }
1494  }
1495 
1496  cnf->dahdiconf = dahdic.confno;
1497 
1498  /* Setup a new channel for playback of audio files */
1499  cnf->chan = ast_request("DAHDI", cap_slin, NULL, chan, "pseudo", NULL);
1500  if (cnf->chan) {
1503  dahdic.chan = 0;
1504  dahdic.confno = cnf->dahdiconf;
1505  dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1506  if (ioctl(ast_channel_fd(cnf->chan, 0), DAHDI_SETCONF, &dahdic)) {
1507  if (test) {
1508  ast_test_status_update(test, "Error setting conference on pseudo channel\n");
1509  }
1510  ast_log(LOG_WARNING, "Error setting conference\n");
1511  if (cnf->chan)
1512  ast_hangup(cnf->chan);
1513  else
1514  close(cnf->fd);
1515  ao2_ref(cnf->usercontainer, -1);
1516  ast_mutex_destroy(&cnf->playlock);
1517  ast_mutex_destroy(&cnf->listenlock);
1518  ast_mutex_destroy(&cnf->recordthreadlock);
1519  ast_mutex_destroy(&cnf->announcethreadlock);
1520  ast_free(cnf);
1521  cnf = NULL;
1522  goto cnfout;
1523  }
1524  }
1525 
1526  /* Fill the conference struct */
1527  cnf->start = time(NULL);
1528  cnf->maxusers = 0x7fffffff;
1529  cnf->isdynamic = dynamic ? 1 : 0;
1530  ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
1531  AST_LIST_INSERT_HEAD(&confs, cnf, list);
1532 
1533  /* Reserve conference number in map */
1534  if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1535  conf_map[confno_int] = 1;
1536 
1537 cnfout:
1538  ao2_cleanup(cap_slin);
1539  if (cnf)
1540  ast_atomic_fetchadd_int(&cnf->refcount, refcount);
1541 
1543 
1544  return cnf;
1545 }
1546 
1547 static char *complete_confno(const char *word, int state)
1548 {
1549  struct ast_conference *cnf;
1550  char *ret = NULL;
1551  int which = 0;
1552  int len = strlen(word);
1553 
1554  AST_LIST_LOCK(&confs);
1555  AST_LIST_TRAVERSE(&confs, cnf, list) {
1556  if (!strncmp(word, cnf->confno, len) && ++which > state) {
1557  /* dup before releasing the lock */
1558  ret = ast_strdup(cnf->confno);
1559  break;
1560  }
1561  }
1563  return ret;
1564 }
1565 
1566 static char *complete_userno(struct ast_conference *cnf, const char *word, int state)
1567 {
1568  char usrno[50];
1569  struct ao2_iterator iter;
1570  struct ast_conf_user *usr;
1571  char *ret = NULL;
1572  int which = 0;
1573  int len = strlen(word);
1574 
1575  iter = ao2_iterator_init(cnf->usercontainer, 0);
1576  for (; (usr = ao2_iterator_next(&iter)); ao2_ref(usr, -1)) {
1577  snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
1578  if (!strncmp(word, usrno, len) && ++which > state) {
1579  ao2_ref(usr, -1);
1580  ret = ast_strdup(usrno);
1581  break;
1582  }
1583  }
1584  ao2_iterator_destroy(&iter);
1585  return ret;
1586 }
1587 
1588 static char *complete_meetmecmd_mute_kick(const char *line, const char *word, int pos, int state)
1589 {
1590  if (pos == 2) {
1591  return complete_confno(word, state);
1592  }
1593  if (pos == 3) {
1594  int len = strlen(word);
1595  char *ret = NULL;
1596  char *saved = NULL;
1597  char *myline;
1598  char *confno;
1599  struct ast_conference *cnf;
1600 
1601  if (!strncasecmp(word, "all", len)) {
1602  if (state == 0) {
1603  return ast_strdup("all");
1604  }
1605  --state;
1606  }
1607 
1608  /* Extract the confno from the command line. */
1609  myline = ast_strdupa(line);
1610  strtok_r(myline, " ", &saved);
1611  strtok_r(NULL, " ", &saved);
1612  confno = strtok_r(NULL, " ", &saved);
1613 
1614  AST_LIST_LOCK(&confs);
1615  AST_LIST_TRAVERSE(&confs, cnf, list) {
1616  if (!strcmp(confno, cnf->confno)) {
1617  ret = complete_userno(cnf, word, state);
1618  break;
1619  }
1620  }
1622 
1623  return ret;
1624  }
1625  return NULL;
1626 }
1627 
1628 static char *complete_meetmecmd_lock(const char *word, int pos, int state)
1629 {
1630  if (pos == 2) {
1631  return complete_confno(word, state);
1632  }
1633  return NULL;
1634 }
1635 
1636 static char *complete_meetmecmd_list(const char *line, const char *word, int pos, int state)
1637 {
1638  int len;
1639 
1640  if (pos == 2) {
1641  len = strlen(word);
1642  if (!strncasecmp(word, STR_CONCISE, len)) {
1643  if (state == 0) {
1644  return ast_strdup(STR_CONCISE);
1645  }
1646  --state;
1647  }
1648 
1649  return complete_confno(word, state);
1650  }
1651  if (pos == 3 && state == 0) {
1652  char *saved = NULL;
1653  char *myline;
1654  char *confno;
1655 
1656  /* Extract the confno from the command line. */
1657  myline = ast_strdupa(line);
1658  strtok_r(myline, " ", &saved);
1659  strtok_r(NULL, " ", &saved);
1660  confno = strtok_r(NULL, " ", &saved);
1661 
1662  if (!strcasecmp(confno, STR_CONCISE)) {
1663  /* There is nothing valid in this position now. */
1664  return NULL;
1665  }
1666 
1667  len = strlen(word);
1668  if (!strncasecmp(word, STR_CONCISE, len)) {
1669  return ast_strdup(STR_CONCISE);
1670  }
1671  }
1672  return NULL;
1673 }
1674 
1675 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1676 {
1677  /* Process the command */
1678  struct ast_conf_user *user;
1679  struct ast_conference *cnf;
1680  int hr, min, sec;
1681  int total = 0;
1682  time_t now;
1683 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
1684 #define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
1685 
1686  switch (cmd) {
1687  case CLI_INIT:
1688  e->command = "meetme list";
1689  e->usage =
1690  "Usage: meetme list [<confno>] [" STR_CONCISE "]\n"
1691  " List all conferences or a specific conference.\n";
1692  return NULL;
1693  case CLI_GENERATE:
1694  return complete_meetmecmd_list(a->line, a->word, a->pos, a->n);
1695  }
1696 
1697  if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], STR_CONCISE))) {
1698  /* List all the conferences */
1699  int concise = (a->argc == 3);
1700  struct ast_str *marked_users;
1701 
1702  if (!(marked_users = ast_str_create(30))) {
1703  return CLI_FAILURE;
1704  }
1705 
1706  now = time(NULL);
1707  AST_LIST_LOCK(&confs);
1708  if (AST_LIST_EMPTY(&confs)) {
1709  if (!concise) {
1710  ast_cli(a->fd, "No active MeetMe conferences.\n");
1711  }
1713  ast_free(marked_users);
1714  return CLI_SUCCESS;
1715  }
1716  if (!concise) {
1717  ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
1718  }
1719  AST_LIST_TRAVERSE(&confs, cnf, list) {
1720  hr = (now - cnf->start) / 3600;
1721  min = ((now - cnf->start) % 3600) / 60;
1722  sec = (now - cnf->start) % 60;
1723  if (!concise) {
1724  if (cnf->markedusers == 0) {
1725  ast_str_set(&marked_users, 0, "N/A ");
1726  } else {
1727  ast_str_set(&marked_users, 0, "%4.4d", cnf->markedusers);
1728  }
1729  ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users,
1730  ast_str_buffer(marked_users), hr, min, sec,
1731  cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
1732  } else {
1733  ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1734  cnf->confno,
1735  cnf->users,
1736  cnf->markedusers,
1737  hr, min, sec,
1738  cnf->isdynamic,
1739  cnf->locked);
1740  }
1741 
1742  total += cnf->users;
1743  }
1745  if (!concise) {
1746  ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1747  }
1748  ast_free(marked_users);
1749  return CLI_SUCCESS;
1750  }
1751  if (a->argc == 3 || (a->argc == 4 && !strcasecmp(a->argv[3], STR_CONCISE))) {
1752  struct ao2_iterator user_iter;
1753  int concise = (a->argc == 4);
1754 
1755  /* List all the users in a conference */
1756  if (AST_LIST_EMPTY(&confs)) {
1757  if (!concise) {
1758  ast_cli(a->fd, "No active MeetMe conferences.\n");
1759  }
1760  return CLI_SUCCESS;
1761  }
1762  /* Find the right conference */
1763  AST_LIST_LOCK(&confs);
1764  AST_LIST_TRAVERSE(&confs, cnf, list) {
1765  if (strcmp(cnf->confno, a->argv[2]) == 0) {
1766  break;
1767  }
1768  }
1769  if (!cnf) {
1770  if (!concise)
1771  ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
1773  return CLI_SUCCESS;
1774  }
1775  /* Show all the users */
1776  time(&now);
1777  user_iter = ao2_iterator_init(cnf->usercontainer, 0);
1778  while((user = ao2_iterator_next(&user_iter))) {
1779  hr = (now - user->jointime) / 3600;
1780  min = ((now - user->jointime) % 3600) / 60;
1781  sec = (now - user->jointime) % 60;
1782  if (!concise) {
1783  ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1784  user->user_no,
1785  S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
1786  S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<no name>"),
1787  ast_channel_name(user->chan),
1788  ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "(Admin)" : "",
1789  ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "(Listen only)" : "",
1790  user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1791  user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1792  istalking(user->talking), hr, min, sec);
1793  } else {
1794  ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1795  user->user_no,
1796  S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, ""),
1797  S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, ""),
1798  ast_channel_name(user->chan),
1799  ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "1" : "",
1800  ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "1" : "",
1801  user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1802  user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1803  user->talking, hr, min, sec);
1804  }
1805  ao2_ref(user, -1);
1806  }
1807  ao2_iterator_destroy(&user_iter);
1808  if (!concise) {
1809  ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
1810  }
1812  return CLI_SUCCESS;
1813  }
1814  return CLI_SHOWUSAGE;
1815 }
1816 
1817 static char *meetme_cmd_helper(struct ast_cli_args *a)
1818 {
1819  /* Process the command */
1820  struct ast_str *cmdline;
1821 
1822  /* Max confno length */
1823  if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1824  return CLI_FAILURE;
1825  }
1826 
1827  ast_str_set(&cmdline, 0, "%s", a->argv[2]); /* Argv 2: conference number */
1828  if (strcasestr(a->argv[1], "lock")) {
1829  if (strcasecmp(a->argv[1], "lock") == 0) {
1830  /* Lock */
1831  ast_str_append(&cmdline, 0, ",L");
1832  } else {
1833  /* Unlock */
1834  ast_str_append(&cmdline, 0, ",l");
1835  }
1836  } else if (strcasestr(a->argv[1], "mute")) {
1837  if (strcasecmp(a->argv[1], "mute") == 0) {
1838  /* Mute */
1839  if (strcasecmp(a->argv[3], "all") == 0) {
1840  ast_str_append(&cmdline, 0, ",N");
1841  } else {
1842  ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);
1843  }
1844  } else {
1845  /* Unmute */
1846  if (strcasecmp(a->argv[3], "all") == 0) {
1847  ast_str_append(&cmdline, 0, ",n");
1848  } else {
1849  ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
1850  }
1851  }
1852  } else if (strcasecmp(a->argv[1], "kick") == 0) {
1853  if (strcasecmp(a->argv[3], "all") == 0) {
1854  /* Kick all */
1855  ast_str_append(&cmdline, 0, ",K");
1856  } else {
1857  /* Kick a single user */
1858  ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
1859  }
1860  } else {
1861  /*
1862  * Should never get here because it is already filtered by the
1863  * callers.
1864  */
1865  ast_free(cmdline);
1866  return CLI_SHOWUSAGE;
1867  }
1868 
1869  ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
1870 
1871  admin_exec(NULL, ast_str_buffer(cmdline));
1872  ast_free(cmdline);
1873 
1874  return CLI_SUCCESS;
1875 }
1876 
1877 static char *meetme_lock_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1878 {
1879  switch (cmd) {
1880  case CLI_INIT:
1881  e->command = "meetme {lock|unlock}";
1882  e->usage =
1883  "Usage: meetme lock|unlock <confno>\n"
1884  " Lock or unlock a conference to new users.\n";
1885  return NULL;
1886  case CLI_GENERATE:
1887  return complete_meetmecmd_lock(a->word, a->pos, a->n);
1888  }
1889 
1890  if (a->argc != 3) {
1891  return CLI_SHOWUSAGE;
1892  }
1893 
1894  return meetme_cmd_helper(a);
1895 }
1896 
1897 static char *meetme_kick_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1898 {
1899  switch (cmd) {
1900  case CLI_INIT:
1901  e->command = "meetme kick";
1902  e->usage =
1903  "Usage: meetme kick <confno> all|<userno>\n"
1904  " Kick a conference or a user in a conference.\n";
1905  return NULL;
1906  case CLI_GENERATE:
1907  return complete_meetmecmd_mute_kick(a->line, a->word, a->pos, a->n);
1908  }
1909 
1910  if (a->argc != 4) {
1911  return CLI_SHOWUSAGE;
1912  }
1913 
1914  return meetme_cmd_helper(a);
1915 }
1916 
1917 static char *meetme_mute_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1918 {
1919  switch (cmd) {
1920  case CLI_INIT:
1921  e->command = "meetme {mute|unmute}";
1922  e->usage =
1923  "Usage: meetme mute|unmute <confno> all|<userno>\n"
1924  " Mute or unmute a conference or a user in a conference.\n";
1925  return NULL;
1926  case CLI_GENERATE:
1927  return complete_meetmecmd_mute_kick(a->line, a->word, a->pos, a->n);
1928  }
1929 
1930  if (a->argc != 4) {
1931  return CLI_SHOWUSAGE;
1932  }
1933 
1934  return meetme_cmd_helper(a);
1935 }
1936 
1937 static struct ast_cli_entry cli_meetme[] = {
1938  AST_CLI_DEFINE(meetme_kick_cmd, "Kick a conference or a user in a conference."),
1939  AST_CLI_DEFINE(meetme_show_cmd, "List all conferences or a specific conference."),
1940  AST_CLI_DEFINE(meetme_lock_cmd, "Lock or unlock a conference to new users."),
1941  AST_CLI_DEFINE(meetme_mute_cmd, "Mute or unmute a conference or a user in a conference."),
1942 };
1943 
1944 static void conf_flush(int fd, struct ast_channel *chan)
1945 {
1946  int x;
1947 
1948  /* read any frames that may be waiting on the channel
1949  and throw them away
1950  */
1951  if (chan) {
1952  struct ast_frame *f;
1953 
1954  /* when no frames are available, this will wait
1955  for 1 millisecond maximum
1956  */
1957  while (ast_waitfor(chan, 1) > 0) {
1958  f = ast_read(chan);
1959  if (f)
1960  ast_frfree(f);
1961  else /* channel was hung up or something else happened */
1962  break;
1963  }
1964  }
1965 
1966  /* flush any data sitting in the pseudo channel */
1967  x = DAHDI_FLUSH_ALL;
1968  if (ioctl(fd, DAHDI_FLUSH, &x))
1969  ast_log(LOG_WARNING, "Error flushing channel\n");
1970 
1971 }
1972 
1973 /*! \brief Remove the conference from the list and free it.
1974 
1975  We assume that this was called while holding conflock. */
1976 static int conf_free(struct ast_conference *conf)
1977 {
1978  int x;
1979  struct announce_listitem *item;
1980 
1981  AST_LIST_REMOVE(&confs, conf, list);
1982 
1983  meetme_stasis_generate_msg(conf, NULL, NULL, meetme_end_type(), NULL);
1984 
1985  if (conf->recording == MEETME_RECORD_ACTIVE) {
1986  conf->recording = MEETME_RECORD_TERMINATE;
1988  while (1) {
1989  usleep(1);
1990  AST_LIST_LOCK(&confs);
1991  if (conf->recording == MEETME_RECORD_OFF)
1992  break;
1994  }
1995  }
1996 
1997  for (x = 0; x < AST_FRAME_BITS; x++) {
1998  if (conf->transframe[x])
1999  ast_frfree(conf->transframe[x]);
2000  if (conf->transpath[x])
2001  ast_translator_free_path(conf->transpath[x]);
2002  }
2003  if (conf->announcethread != AST_PTHREADT_NULL) {
2004  ast_mutex_lock(&conf->announcelistlock);
2005  conf->announcethread_stop = 1;
2007  ast_cond_signal(&conf->announcelist_addition);
2008  ast_mutex_unlock(&conf->announcelistlock);
2009  pthread_join(conf->announcethread, NULL);
2010 
2011  while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
2012  /* If it's a voicemail greeting file we don't want to remove it */
2013  if (!item->vmrec){
2014  ast_filedelete(item->namerecloc, NULL);
2015  }
2016  ao2_ref(item, -1);
2017  }
2018  ast_mutex_destroy(&conf->announcelistlock);
2019  }
2020 
2021  if (conf->origframe)
2022  ast_frfree(conf->origframe);
2023  ast_hangup(conf->lchan);
2024  ast_hangup(conf->chan);
2025  if (conf->fd >= 0)
2026  close(conf->fd);
2027  if (conf->recordingfilename) {
2028  ast_free(conf->recordingfilename);
2029  }
2030  if (conf->usercontainer) {
2031  ao2_ref(conf->usercontainer, -1);
2032  }
2033  if (conf->recordingformat) {
2034  ast_free(conf->recordingformat);
2035  }
2036  ast_mutex_destroy(&conf->playlock);
2037  ast_mutex_destroy(&conf->listenlock);
2038  ast_mutex_destroy(&conf->recordthreadlock);
2039  ast_mutex_destroy(&conf->announcethreadlock);
2040  ast_free(conf);
2041 
2042  return 0;
2043 }
2044 
2045 static void conf_queue_dtmf(const struct ast_conference *conf,
2046  const struct ast_conf_user *sender, struct ast_frame *f)
2047 {
2048  struct ast_conf_user *user;
2049  struct ao2_iterator user_iter;
2050 
2051  user_iter = ao2_iterator_init(conf->usercontainer, 0);
2052  while ((user = ao2_iterator_next(&user_iter))) {
2053  if (user == sender) {
2054  ao2_ref(user, -1);
2055  continue;
2056  }
2057  if (ast_write(user->chan, f) < 0)
2058  ast_log(LOG_WARNING, "Error writing frame to channel %s\n", ast_channel_name(user->chan));
2059  ao2_ref(user, -1);
2060  }
2061  ao2_iterator_destroy(&user_iter);
2062 }
2063 
2064 /*! \brief Decrement reference counts, as incremented by find_conf() */
2065 static int dispose_conf(struct ast_conference *conf)
2066 {
2067  int res = 0;
2068  int confno_int = 0;
2069 
2070  AST_LIST_LOCK(&confs);
2071  if (ast_atomic_dec_and_test(&conf->refcount)) {
2072  /* Take the conference room number out of an inuse state */
2073  if ((sscanf(conf->confno, "%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
2074  conf_map[confno_int] = 0;
2075  }
2076  conf_free(conf);
2077  res = 1;
2078  }
2080 
2081  return res;
2082 }
2083 
2084 static int rt_extend_conf(const char *confno)
2085 {
2086  char currenttime[32];
2087  char endtime[32];
2088  struct timeval now;
2089  struct ast_tm tm;
2090  struct ast_variable *var, *orig_var;
2091  char bookid[51];
2092 
2093  if (!extendby) {
2094  return 0;
2095  }
2096 
2097  now = ast_tvnow();
2098 
2099  ast_localtime(&now, &tm, NULL);
2100  ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2101 
2102  var = ast_load_realtime("meetme", "confno",
2103  confno, "startTime<= ", currenttime,
2104  "endtime>= ", currenttime, NULL);
2105 
2106  orig_var = var;
2107 
2108  /* Identify the specific RealTime conference */
2109  while (var) {
2110  if (!strcasecmp(var->name, "bookid")) {
2111  ast_copy_string(bookid, var->value, sizeof(bookid));
2112  }
2113  if (!strcasecmp(var->name, "endtime")) {
2114  ast_copy_string(endtime, var->value, sizeof(endtime));
2115  }
2116 
2117  var = var->next;
2118  }
2119  ast_variables_destroy(orig_var);
2120 
2121  ast_strptime(endtime, DATE_FORMAT, &tm);
2122  now = ast_mktime(&tm, NULL);
2123 
2124  now.tv_sec += extendby;
2125 
2126  ast_localtime(&now, &tm, NULL);
2127  ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2128  strcat(currenttime, "0"); /* Seconds needs to be 00 */
2129 
2130  var = ast_load_realtime("meetme", "confno",
2131  confno, "startTime<= ", currenttime,
2132  "endtime>= ", currenttime, NULL);
2133 
2134  /* If there is no conflict with extending the conference, update the DB */
2135  if (!var) {
2136  ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
2137  ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
2138  return 0;
2139 
2140  }
2141 
2142  ast_variables_destroy(var);
2143  return -1;
2144 }
2145 
2146 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
2147 {
2148  char *original_moh;
2149 
2150  ast_channel_lock(chan);
2151  original_moh = ast_strdupa(ast_channel_musicclass(chan));
2152  ast_channel_musicclass_set(chan, musicclass);
2153  ast_channel_unlock(chan);
2154 
2155  ast_moh_start(chan, original_moh, NULL);
2156 
2157  ast_channel_lock(chan);
2158  ast_channel_musicclass_set(chan, original_moh);
2159  ast_channel_unlock(chan);
2160 }
2161 
2162 static const char *get_announce_filename(enum announcetypes type)
2163 {
2164  switch (type) {
2165  case CONF_HASLEFT:
2166  return "conf-hasleft";
2167  break;
2168  case CONF_HASJOIN:
2169  return "conf-hasjoin";
2170  break;
2171  default:
2172  return "";
2173  }
2174 }
2175 
2176 static void *announce_thread(void *data)
2177 {
2178  struct announce_listitem *current;
2179  struct ast_conference *conf = data;
2180  int res;
2181  char filename[PATH_MAX] = "";
2183  AST_LIST_HEAD_INIT_NOLOCK(&local_list);
2184 
2185  while (!conf->announcethread_stop) {
2186  ast_mutex_lock(&conf->announcelistlock);
2187  if (conf->announcethread_stop) {
2188  ast_mutex_unlock(&conf->announcelistlock);
2189  break;
2190  }
2191  if (AST_LIST_EMPTY(&conf->announcelist))
2192  ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
2193 
2194  AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
2195  AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2196 
2197  ast_mutex_unlock(&conf->announcelistlock);
2198  if (conf->announcethread_stop) {
2199  break;
2200  }
2201 
2202  for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
2203  ast_debug(1, "About to play %s\n", current->namerecloc);
2204  if (!ast_fileexists(current->namerecloc, NULL, NULL))
2205  continue;
2206  if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
2207  if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
2208  res = ast_waitstream(current->confchan, "");
2209  if (!res) {
2210  ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
2211  if (!ast_streamfile(current->confchan, filename, current->language))
2212  ast_waitstream(current->confchan, "");
2213  }
2214  }
2215  if (current->announcetype == CONF_HASLEFT && current->announcetype && !current->vmrec) {
2216  /* only remove it if it isn't a VM recording file */
2217  ast_filedelete(current->namerecloc, NULL);
2218  }
2219  }
2220  }
2221 
2222  /* thread marked to stop, clean up */
2223  while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
2224  /* only delete if it's a vm rec */
2225  if (!current->vmrec) {
2226  ast_filedelete(current->namerecloc, NULL);
2227  }
2228  ao2_ref(current, -1);
2229  }
2230  return NULL;
2231 }
2232 
2233 static int can_write(struct ast_channel *chan, struct ast_flags64 *confflags)
2234 {
2235  if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
2236  return 1;
2237  }
2238 
2239  return (ast_channel_state(chan) == AST_STATE_UP);
2240 }
2241 
2242 static void send_talking_event(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking)
2243 {
2244  RAII_VAR(struct ast_json *, status_blob, status_to_json(talking), ast_json_unref);
2245  meetme_stasis_generate_msg(conf, chan, user, meetme_talking_type(), status_blob);
2246 }
2247 
2248 static void set_user_talking(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor)
2249 {
2250  int last_talking = user->talking;
2251  if (last_talking == talking)
2252  return;
2253 
2254  user->talking = talking;
2255 
2256  if (monitor) {
2257  /* Check if talking state changed. Take care of -1 which means unmonitored */
2258  int was_talking = (last_talking > 0);
2259  int now_talking = (talking > 0);
2260  if (was_talking != now_talking) {
2261  send_talking_event(chan, conf, user, now_talking);
2262  }
2263  }
2264 }
2265 
2266 static int user_set_hangup_cb(void *obj, void *check_admin_arg, int flags)
2267 {
2268  struct ast_conf_user *user = obj;
2269  /* actual pointer contents of check_admin_arg is irrelevant */
2270 
2271  if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
2272  user->adminflags |= ADMINFLAG_HANGUP;
2273  }
2274  return 0;
2275 }
2276 
2277 static int user_set_kickme_cb(void *obj, void *check_admin_arg, int flags)
2278 {
2279  struct ast_conf_user *user = obj;
2280  /* actual pointer contents of check_admin_arg is irrelevant */
2281 
2282  if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
2283  user->adminflags |= ADMINFLAG_KICKME;
2284  }
2285  return 0;
2286 }
2287 
2288 static int user_set_unmuted_cb(void *obj, void *check_admin_arg, int flags)
2289 {
2290  struct ast_conf_user *user = obj;
2291  /* actual pointer contents of check_admin_arg is irrelevant */
2292 
2293  if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
2295  }
2296  return 0;
2297 }
2298 
2299 static int user_set_muted_cb(void *obj, void *check_admin_arg, int flags)
2300 {
2301  struct ast_conf_user *user = obj;
2302  /* actual pointer contents of check_admin_arg is irrelevant */
2303 
2304  if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
2305  user->adminflags |= ADMINFLAG_MUTED;
2306  }
2307  return 0;
2308 }
2309 
2310 enum menu_modes {
2311  MENU_DISABLED = 0,
2312  MENU_NORMAL,
2313  MENU_ADMIN,
2314  MENU_ADMIN_EXTENDED,
2315 };
2316 
2317 /*! \internal
2318  * \brief Processes menu options for the standard menu (accessible through the 's' option for app_meetme)
2319  *
2320  * \param menu_mode a pointer to the currently active menu_mode.
2321  * \param dtmf a pointer to the dtmf value currently being processed against the menu.
2322  * \param conf the active conference for which the user has called the menu from.
2323  * \param confflags flags used by conf for various options
2324  * \param chan ast_channel belonging to the user who called the menu
2325  * \param user which meetme conference user invoked the menu
2326  */
2327 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)
2328 {
2329  switch (*dtmf) {
2330  case '1': /* Un/Mute */
2331  *menu_mode = MENU_DISABLED;
2332 
2333  /* user can only toggle the self-muted state */
2335 
2336  /* they can't override the admin mute state */
2337  if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2338  if (!ast_streamfile(chan, "conf-muted", ast_channel_language(chan))) {
2339  ast_waitstream(chan, "");
2340  }
2341  } else {
2342  if (!ast_streamfile(chan, "conf-unmuted", ast_channel_language(chan))) {
2343  ast_waitstream(chan, "");
2344  }
2345  }
2346  break;
2347 
2348  case '2':
2349  *menu_mode = MENU_DISABLED;
2352  }
2353 
2354  if (user->adminflags & ADMINFLAG_T_REQUEST) {
2355  if (!ast_streamfile(chan, "beep", ast_channel_language(chan))) {
2356  ast_waitstream(chan, "");
2357  }
2358  }
2359  break;
2360 
2361  case '4':
2362  tweak_listen_volume(user, VOL_DOWN);
2363  break;
2364  case '5':
2365  /* Extend RT conference */
2366  if (rt_schedule) {
2367  rt_extend_conf(conf->confno);
2368  }
2369  *menu_mode = MENU_DISABLED;
2370  break;
2371 
2372  case '6':
2373  tweak_listen_volume(user, VOL_UP);
2374  break;
2375 
2376  case '7':
2377  tweak_talk_volume(user, VOL_DOWN);
2378  break;
2379 
2380  case '8':
2381  *menu_mode = MENU_DISABLED;
2382  break;
2383 
2384  case '9':
2385  tweak_talk_volume(user, VOL_UP);
2386  break;
2387 
2388  default:
2389  *menu_mode = MENU_DISABLED;
2390  if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
2391  ast_waitstream(chan, "");
2392  }
2393  break;
2394  }
2395 }
2396 
2397 /*! \internal
2398  * \brief Processes menu options for the administrator menu (accessible through the 's' option for app_meetme)
2399  *
2400  * \param menu_mode a pointer to the currently active menu_mode.
2401  * \param dtmf a pointer to the dtmf value currently being processed against the menu.
2402  * \param conf the active conference for which the user has called the menu from.
2403  * \param confflags flags used by conf for various options
2404  * \param chan ast_channel belonging to the user who called the menu
2405  * \param user which meetme conference user invoked the menu
2406  */
2407 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)
2408 {
2409  switch(*dtmf) {
2410  case '1': /* Un/Mute */
2411  *menu_mode = MENU_DISABLED;
2412  /* for admin, change both admin and use flags */
2415  } else {
2417  }
2418 
2419  if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2420  if (!ast_streamfile(chan, "conf-muted", ast_channel_language(chan))) {
2421  ast_waitstream(chan, "");
2422  }
2423  } else {
2424  if (!ast_streamfile(chan, "conf-unmuted", ast_channel_language(chan))) {
2425  ast_waitstream(chan, "");
2426  }
2427  }
2428  break;
2429 
2430  case '2': /* Un/Lock the Conference */
2431  *menu_mode = MENU_DISABLED;
2432  if (conf->locked) {
2433  conf->locked = 0;
2434  if (!ast_streamfile(chan, "conf-unlockednow", ast_channel_language(chan))) {
2435  ast_waitstream(chan, "");
2436  }
2437  } else {
2438  conf->locked = 1;
2439  if (!ast_streamfile(chan, "conf-lockednow", ast_channel_language(chan))) {
2440  ast_waitstream(chan, "");
2441  }
2442  }
2443  break;
2444 
2445  case '3': /* Eject last user */
2446  {
2447  struct ast_conf_user *usr = NULL;
2448  int max_no = 0;
2449  ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
2450  *menu_mode = MENU_DISABLED;
2451  usr = ao2_find(conf->usercontainer, &max_no, 0);
2452  if ((ast_channel_name(usr->chan) == ast_channel_name(chan)) || ast_test_flag64(&usr->userflags, CONFFLAG_ADMIN)) {
2453  if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
2454  ast_waitstream(chan, "");
2455  }
2456  } else {
2457  usr->adminflags |= ADMINFLAG_KICKME;
2458  }
2459  ao2_ref(usr, -1);
2460  ast_stopstream(chan);
2461  break;
2462  }
2463 
2464  case '4':
2465  tweak_listen_volume(user, VOL_DOWN);
2466  break;
2467 
2468  case '5':
2469  /* Extend RT conference */
2470  if (rt_schedule) {
2471  if (!rt_extend_conf(conf->confno)) {
2472  if (!ast_streamfile(chan, "conf-extended", ast_channel_language(chan))) {
2473  ast_waitstream(chan, "");
2474  }
2475  } else {
2476  if (!ast_streamfile(chan, "conf-nonextended", ast_channel_language(chan))) {
2477  ast_waitstream(chan, "");
2478  }
2479  }
2480  ast_stopstream(chan);
2481  }
2482  *menu_mode = MENU_DISABLED;
2483  break;
2484 
2485  case '6':
2486  tweak_listen_volume(user, VOL_UP);
2487  break;
2488 
2489  case '7':
2490  tweak_talk_volume(user, VOL_DOWN);
2491  break;
2492 
2493  case '8':
2494  if (!ast_streamfile(chan, "conf-adminmenu-menu8", ast_channel_language(chan))) {
2495  /* 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. */
2496  *dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2497  ast_stopstream(chan);
2498  }
2499  *menu_mode = MENU_ADMIN_EXTENDED;
2500  break;
2501 
2502  case '9':
2503  tweak_talk_volume(user, VOL_UP);
2504  break;
2505  default:
2506  *menu_mode = MENU_DISABLED;
2507  /* Play an error message! */
2508  if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
2509  ast_waitstream(chan, "");
2510  }
2511  break;
2512  }
2513 
2514 }
2515 
2516 /*! \internal
2517  * \brief Processes menu options for the extended administrator menu (accessible through option 8 on the administrator menu)
2518  *
2519  * \param menu_mode a pointer to the currently active menu_mode.
2520  * \param dtmf a pointer to the dtmf value currently being processed against the menu.
2521  * \param conf the active conference for which the user has called the menu from.
2522  * \param confflags flags used by conf for various options
2523  * \param chan ast_channel belonging to the user who called the menu
2524  * \param user which meetme conference user invoked the menu
2525  * \param recordingtmp, recordingtmp_size character buffer which may hold the name of the conference recording file
2526  * \param cap_slin
2527  */
2528 static void meetme_menu_admin_extended(enum menu_modes *menu_mode, int *dtmf,
2529  struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan,
2530  struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size,
2531  struct ast_format_cap *cap_slin)
2532 {
2533  int keepplaying;
2534  int playednamerec;
2535  int res;
2536  struct ao2_iterator user_iter;
2537  struct ast_conf_user *usr = NULL;
2538 
2539  switch(*dtmf) {
2540  case '1': /* *81 Roll call */
2541  keepplaying = 1;
2542  playednamerec = 0;
2543  if (conf->users == 1) {
2544  if (keepplaying && !ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan))) {
2545  res = ast_waitstream(chan, AST_DIGIT_ANY);
2546  ast_stopstream(chan);
2547  if (res > 0) {
2548  keepplaying = 0;
2549  }
2550  }
2551  } else if (conf->users == 2) {
2552  if (keepplaying && !ast_streamfile(chan, "conf-onlyone", ast_channel_language(chan))) {
2553  res = ast_waitstream(chan, AST_DIGIT_ANY);
2554  ast_stopstream(chan);
2555  if (res > 0) {
2556  keepplaying = 0;
2557  }
2558  }
2559  } else {
2560  if (keepplaying && !ast_streamfile(chan, "conf-thereare", ast_channel_language(chan))) {
2561  res = ast_waitstream(chan, AST_DIGIT_ANY);
2562  ast_stopstream(chan);
2563  if (res > 0) {
2564  keepplaying = 0;
2565  }
2566  }
2567  if (keepplaying) {
2568  res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
2569  ast_stopstream(chan);
2570  if (res > 0) {
2571  keepplaying = 0;
2572  }
2573  }
2574  if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", ast_channel_language(chan))) {
2575  res = ast_waitstream(chan, AST_DIGIT_ANY);
2576  ast_stopstream(chan);
2577  if (res > 0) {
2578  keepplaying = 0;
2579  }
2580  }
2581  }
2582  user_iter = ao2_iterator_init(conf->usercontainer, 0);
2583  while((usr = ao2_iterator_next(&user_iter))) {
2584  if (ast_fileexists(usr->namerecloc, NULL, NULL)) {
2585  if (keepplaying && !ast_streamfile(chan, usr->namerecloc, ast_channel_language(chan))) {
2586  res = ast_waitstream(chan, AST_DIGIT_ANY);
2587  ast_stopstream(chan);
2588  if (res > 0) {
2589  keepplaying = 0;
2590  }
2591  }
2592  playednamerec = 1;
2593  }
2594  ao2_ref(usr, -1);
2595  }
2596  ao2_iterator_destroy(&user_iter);
2597  if (keepplaying && playednamerec && !ast_streamfile(chan, "conf-roll-callcomplete", ast_channel_language(chan))) {
2598  res = ast_waitstream(chan, AST_DIGIT_ANY);
2599  ast_stopstream(chan);
2600  if (res > 0) {
2601  keepplaying = 0;
2602  }
2603  }
2604 
2605  *menu_mode = MENU_DISABLED;
2606  break;
2607 
2608  case '2': /* *82 Eject all non-admins */
2609  if (conf->users == 1) {
2610  if(!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
2611  ast_waitstream(chan, "");
2612  }
2613  } else {
2614  ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_kickme_cb, &conf);
2615  }
2616  ast_stopstream(chan);
2617  *menu_mode = MENU_DISABLED;
2618  break;
2619 
2620  case '3': /* *83 (Admin) mute/unmute all non-admins */
2621  if(conf->gmuted) {
2622  conf->gmuted = 0;
2623  ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, &conf);
2624  if (!ast_streamfile(chan, "conf-now-unmuted", ast_channel_language(chan))) {
2625  ast_waitstream(chan, "");
2626  }
2627  } else {
2628  conf->gmuted = 1;
2629  ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_muted_cb, &conf);
2630  if (!ast_streamfile(chan, "conf-now-muted", ast_channel_language(chan))) {
2631  ast_waitstream(chan, "");
2632  }
2633  }
2634  ast_stopstream(chan);
2635  *menu_mode = MENU_DISABLED;
2636  break;
2637 
2638  case '4': /* *84 Record conference */
2639  if (conf->recording != MEETME_RECORD_ACTIVE) {
2640  ast_set_flag64(confflags, CONFFLAG_RECORDCONF);
2641  if (!conf->recordingfilename) {
2642  const char *var;
2643  ast_channel_lock(chan);
2644  if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
2645  conf->recordingfilename = ast_strdup(var);
2646  }
2647  if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
2648  conf->recordingformat = ast_strdup(var);
2649  }
2650  ast_channel_unlock(chan);
2651  if (!conf->recordingfilename) {
2652  snprintf(recordingtmp, recordingtmp_size, "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan));
2653  conf->recordingfilename = ast_strdup(recordingtmp);
2654  }
2655  if (!conf->recordingformat) {
2656  conf->recordingformat = ast_strdup("wav");
2657  }
2658  ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
2659  conf->confno, conf->recordingfilename, conf->recordingformat);
2660  }
2661 
2662  ast_mutex_lock(&conf->recordthreadlock);
2663  if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", cap_slin, NULL, chan, "pseudo", NULL)))) {
2664  struct dahdi_confinfo dahdic;
2665 
2668  dahdic.chan = 0;
2669  dahdic.confno = conf->dahdiconf;
2670  dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
2671  if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, &dahdic)) {
2672  ast_log(LOG_WARNING, "Error starting listen channel\n");
2673  ast_hangup(conf->lchan);
2674  conf->lchan = NULL;
2675  } else {
2676  ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
2677  }
2678  }
2679  ast_mutex_unlock(&conf->recordthreadlock);
2680  if (!ast_streamfile(chan, "conf-now-recording", ast_channel_language(chan))) {
2681  ast_waitstream(chan, "");
2682  }
2683  }
2684 
2685  ast_stopstream(chan);
2686  *menu_mode = MENU_DISABLED;
2687  break;
2688 
2689  case '8': /* *88 Exit the menu and return to the conference... without an error message */
2690  ast_stopstream(chan);
2691  *menu_mode = MENU_DISABLED;
2692  break;
2693 
2694  default:
2695  if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
2696  ast_waitstream(chan, "");
2697  }
2698  ast_stopstream(chan);
2699  *menu_mode = MENU_DISABLED;
2700  break;
2701  }
2702 }
2703 
2704 /*! \internal
2705  * \brief Processes menu options for the various menu types (accessible through the 's' option for app_meetme)
2706  *
2707  * \param menu_mode a pointer to the currently active menu_mode.
2708  * \param dtmf a pointer to the dtmf value currently being processed against the menu.
2709  * \param conf the active conference for which the user has called the menu from.
2710  * \param confflags flags used by conf for various options
2711  * \param chan ast_channel belonging to the user who called the menu
2712  * \param user which meetme conference user invoked the menu
2713  * \param recordingtmp,recordingtmp_size character buffer which may hold the name of the conference recording file
2714  * \param cap_slin
2715  */
2716 static void meetme_menu(enum menu_modes *menu_mode, int *dtmf,
2717  struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan,
2718  struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size,
2719  struct ast_format_cap *cap_slin)
2720 {
2721  switch (*menu_mode) {
2722  case MENU_DISABLED:
2723  break;
2724  case MENU_NORMAL:
2725  meetme_menu_normal(menu_mode, dtmf, conf, confflags, chan, user);
2726  break;
2727  case MENU_ADMIN:
2728  meetme_menu_admin(menu_mode, dtmf, conf, confflags, chan, user);
2729  /* Admin Menu is capable of branching into another menu, in which case it will reset dtmf and change the menu mode. */
2730  if (*menu_mode != MENU_ADMIN_EXTENDED || (*dtmf <= 0)) {
2731  break;
2732  }
2733  case MENU_ADMIN_EXTENDED:
2734  meetme_menu_admin_extended(menu_mode, dtmf, conf, confflags, chan, user,
2735  recordingtmp, recordingtmp_size, cap_slin);
2736  break;
2737  }
2738 }
2739 
2740 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struct ast_flags64 *confflags, char *optargs[])
2741 {
2742  struct ast_conf_user *user = NULL;
2743  int fd;
2744  struct dahdi_confinfo dahdic, dahdic_empty;
2745  struct ast_frame *f;
2746  struct ast_channel *c;
2747  struct ast_frame fr;
2748  int outfd;
2749  int ms;
2750  int nfds;
2751  int res;
2752  int retrydahdi;
2753  int origfd;
2754  int musiconhold = 0, mohtempstopped = 0;
2755  int firstpass = 0;
2756  int lastmarked = 0;
2757  int currentmarked = 0;
2758  int ret = -1;
2759  int x;
2760  enum menu_modes menu_mode = MENU_DISABLED;
2761  int talkreq_manager = 0;
2762  int using_pseudo = 0;
2763  int duration = 20;
2764  int sent_event = 0;
2765  int checked = 0;
2766  int announcement_played = 0;
2767  struct timeval now;
2768  struct ast_dsp *dsp = NULL;
2769  struct ast_app *agi_app;
2770  char *agifile;
2771  const char *agifiledefault = "conf-background.agi", *tmpvar;
2772  char meetmesecs[30] = "";
2773  char exitcontext[AST_MAX_CONTEXT] = "";
2774  char recordingtmp[AST_MAX_EXTENSION * 2] = "";
2775  char members[10] = "";
2776  int dtmf = 0, opt_waitmarked_timeout = 0;
2777  time_t timeout = 0;
2778  struct dahdi_bufferinfo bi;
2779  char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
2780  char *buf = __buf + AST_FRIENDLY_OFFSET;
2781  char *exitkeys = NULL;
2782  unsigned int calldurationlimit = 0;
2783  long timelimit = 0;
2784  long play_warning = 0;
2785  long warning_freq = 0;
2786  const char *warning_sound = NULL;
2787  const char *end_sound = NULL;
2788  char *parse;
2789  long time_left_ms = 0;
2790  struct timeval nexteventts = { 0, };
2791  int to;
2792  int setusercount = 0;
2793  int confsilence = 0, totalsilence = 0;
2794  char *mailbox, *context;
2796 
2797  if (!cap_slin) {
2798  goto conf_run_cleanup;
2799  }
2800  ast_format_cap_append(cap_slin, ast_format_slin, 0);
2801 
2802  if (!(user = ao2_alloc(sizeof(*user), NULL))) {
2803  goto conf_run_cleanup;
2804  }
2805 
2806  /* Possible timeout waiting for marked user */
2807  if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
2808  !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
2809  (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
2810  (opt_waitmarked_timeout > 0)) {
2811  timeout = time(NULL) + opt_waitmarked_timeout;
2812  }
2813 
2814  if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
2815  calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
2816  ast_verb(3, "Setting call duration limit to %u seconds.\n", calldurationlimit);
2817  }
2818 
2819  if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
2820  char *limit_str, *warning_str, *warnfreq_str;
2821  const char *var;
2822 
2823  parse = optargs[OPT_ARG_DURATION_LIMIT];
2824  limit_str = strsep(&parse, ":");
2825  warning_str = strsep(&parse, ":");
2826  warnfreq_str = parse;
2827 
2828  timelimit = atol(limit_str);
2829  if (warning_str)
2830  play_warning = atol(warning_str);
2831  if (warnfreq_str)
2832  warning_freq = atol(warnfreq_str);
2833 
2834  if (!timelimit) {
2835  timelimit = play_warning = warning_freq = 0;
2836  warning_sound = NULL;
2837  } else if (play_warning > timelimit) {
2838  if (!warning_freq) {
2839  play_warning = 0;
2840  } else {
2841  while (play_warning > timelimit)
2842  play_warning -= warning_freq;
2843  if (play_warning < 1)
2844  play_warning = warning_freq = 0;
2845  }
2846  }
2847 
2848  ast_verb(3, "Setting conference duration limit to: %ldms.\n", timelimit);
2849  if (play_warning) {
2850  ast_verb(3, "Setting warning time to %ldms from the conference duration limit.\n", play_warning);
2851  }
2852  if (warning_freq) {
2853  ast_verb(3, "Setting warning frequency to %ldms.\n", warning_freq);
2854  }
2855 
2856  ast_channel_lock(chan);
2857  if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
2858  var = ast_strdupa(var);
2859  }
2860  ast_channel_unlock(chan);
2861 
2862  warning_sound = var ? var : "timeleft";
2863 
2864  ast_channel_lock(chan);
2865  if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
2866  var = ast_strdupa(var);
2867  }
2868  ast_channel_unlock(chan);
2869 
2870  end_sound = var ? var : NULL;
2871 
2872  /* undo effect of S(x) in case they are both used */
2873  calldurationlimit = 0;
2874  /* more efficient do it like S(x) does since no advanced opts */
2875  if (!play_warning && !end_sound && timelimit) {
2876  calldurationlimit = timelimit / 1000;
2877  timelimit = play_warning = warning_freq = 0;
2878  } else {
2879  ast_debug(2, "Limit Data for this call:\n");
2880  ast_debug(2, "- timelimit = %ld\n", timelimit);
2881  ast_debug(2, "- play_warning = %ld\n", play_warning);
2882  ast_debug(2, "- warning_freq = %ld\n", warning_freq);
2883  ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
2884  ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
2885  }
2886  }
2887 
2888  /* Get exit keys */
2889  if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
2890  if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
2891  exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
2892  else
2893  exitkeys = ast_strdupa("#"); /* Default */
2894  }
2895 
2896  if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
2897  if (!conf->recordingfilename) {
2898  const char *var;
2899  ast_channel_lock(chan);
2900  if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
2901  conf->recordingfilename = ast_strdup(var);
2902  }
2903  if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
2904  conf->recordingformat = ast_strdup(var);
2905  }
2906  ast_channel_unlock(chan);
2907  if (!conf->recordingfilename) {
2908  snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan));
2909  conf->recordingfilename = ast_strdup(recordingtmp);
2910  }
2911  if (!conf->recordingformat) {
2912  conf->recordingformat = ast_strdup("wav");
2913  }
2914  ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
2915  conf->confno, conf->recordingfilename, conf->recordingformat);
2916  }
2917  }
2918 
2919  ast_mutex_lock(&conf->recordthreadlock);
2920  if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
2921  ((conf->lchan = ast_request("DAHDI", cap_slin, NULL, chan, "pseudo", NULL)))) {
2924  dahdic.chan = 0;
2925  dahdic.confno = conf->dahdiconf;
2926  dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
2927  if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, &dahdic)) {
2928  ast_log(LOG_WARNING, "Error starting listen channel\n");
2929  ast_hangup(conf->lchan);
2930  conf->lchan = NULL;
2931  } else {
2932  ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
2933  }
2934  }
2935  ast_mutex_unlock(&conf->recordthreadlock);
2936 
2937  ast_mutex_lock(&conf->announcethreadlock);
2938  if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
2939  ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC)) {
2940  ast_mutex_init(&conf->announcelistlock);
2941  AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2942  ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
2943  }
2944  ast_mutex_unlock(&conf->announcethreadlock);
2945 
2946  time(&user->jointime);
2947 
2948  user->timelimit = timelimit;
2949  user->play_warning = play_warning;
2950  user->warning_freq = warning_freq;
2951  user->warning_sound = warning_sound;
2952  user->end_sound = end_sound;
2953 
2954  if (calldurationlimit > 0) {
2955  time(&user->kicktime);
2956  user->kicktime = user->kicktime + calldurationlimit;
2957  }
2958 
2959  if (ast_tvzero(user->start_time))
2960  user->start_time = ast_tvnow();
2961  time_left_ms = user->timelimit;
2962 
2963  if (user->timelimit) {
2964  nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2965  nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
2966  }
2967 
2968  if (conf->locked && (!ast_test_flag64(confflags, CONFFLAG_ADMIN))) {
2969  /* Sorry, but this conference is locked! */
2970  if (!ast_streamfile(chan, "conf-locked", ast_channel_language(chan)))
2971  ast_waitstream(chan, "");
2972  goto outrun;
2973  }
2974 
2975  ast_mutex_lock(&conf->playlock);
2976 
2977  if (rt_schedule && conf->maxusers) {
2978  if (conf->users >= conf->maxusers) {
2979  /* Sorry, but this confernce has reached the participant limit! */
2980  ast_mutex_unlock(&conf->playlock);
2981  if (!ast_streamfile(chan, "conf-full", ast_channel_language(chan)))
2982  ast_waitstream(chan, "");
2983  goto outrun;
2984  }
2985  }
2986 
2987  ao2_lock(conf->usercontainer);
2988  ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &user->user_no);
2989  user->user_no++;
2990  ao2_link(conf->usercontainer, user);
2991  ao2_unlock(conf->usercontainer);
2992 
2993  user->chan = chan;
2994  user->userflags = *confflags;
2995  user->adminflags = ast_test_flag64(confflags, CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
2996  if (!ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
2997  user->adminflags |= (conf->gmuted) ? ADMINFLAG_MUTED : 0;
2998  }
2999  user->talking = -1;
3000 
3001  ast_mutex_unlock(&conf->playlock);
3002 
3003  if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC))) {
3004  char destdir[PATH_MAX];
3005 
3006  snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
3007 
3008  if (ast_mkdir(destdir, 0777) != 0) {
3009  ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
3010  goto outrun;
3011  }
3012 
3013  if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
3014  context = ast_strdupa(optargs[OPT_ARG_INTROUSER_VMREC]);
3015  mailbox = strsep(&context, "@");
3016 
3017  if (ast_strlen_zero(mailbox)) {
3018  /* invalid input, clear the v flag*/
3019  ast_clear_flag64(confflags,CONFFLAG_INTROUSER_VMREC);
3020  ast_log(LOG_WARNING,"You must specify a mailbox in the v() option\n");
3021  } else {
3022  if (ast_strlen_zero(context)) {
3023  context = "default";
3024  }
3025  /* if there is no mailbox we don't need to do this logic */
3026  snprintf(user->namerecloc, sizeof(user->namerecloc),
3027  "%s/voicemail/%s/%s/greet",ast_config_AST_SPOOL_DIR,context,mailbox);
3028 
3029  /* if the greeting doesn't exist then use the temp file method instead, clear flag v */
3030  if (!ast_fileexists(user->namerecloc, NULL, NULL)){
3031  snprintf(user->namerecloc, sizeof(user->namerecloc),
3032  "%s/meetme-username-%s-%d", destdir,
3033  conf->confno, user->user_no);
3034  ast_clear_flag64(confflags, CONFFLAG_INTROUSER_VMREC);
3035  }
3036  }
3037  } else {
3038  snprintf(user->namerecloc, sizeof(user->namerecloc),
3039  "%s/meetme-username-%s-%d", destdir,
3040  conf->confno, user->user_no);
3041  }
3042 
3043  res = 0;
3044  if (ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) && !ast_fileexists(user->namerecloc, NULL, NULL))
3045  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);
3046  else if (ast_test_flag64(confflags, CONFFLAG_INTROUSER) && !ast_fileexists(user->namerecloc, NULL, NULL))
3047  res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
3048  if (res == -1)
3049  goto outrun;
3050 
3051  }
3052 
3053  ast_mutex_lock(&conf->playlock);
3054 
3055  if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER))
3056  conf->markedusers++;
3057  conf->users++;
3058  if (rt_log_members) {
3059  /* Update table */
3060  snprintf(members, sizeof(members), "%d", conf->users);
3061  ast_realtime_require_field("meetme",
3062  "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
3063  "members", RQ_UINTEGER1, strlen(members),
3064  NULL);
3065  ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
3066  }
3067  setusercount = 1;
3068 
3069  /* This device changed state now - if this is the first user */
3070  if (conf->users == 1)
3072 
3073  ast_mutex_unlock(&conf->playlock);
3074 
3075  /* return the unique ID of the conference */
3076  pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
3077 
3078  if (ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT)) {
3079  ast_channel_lock(chan);
3080  if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
3081  ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
3082  } else {
3083  ast_copy_string(exitcontext, ast_channel_context(chan), sizeof(exitcontext));
3084  }
3085  ast_channel_unlock(chan);
3086  }
3087 
3088  /* Play an arbitrary intro message */
3089  if (ast_test_flag64(confflags, CONFFLAG_INTROMSG) &&
3090  !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
3091  if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], ast_channel_language(chan))) {
3092  ast_waitstream(chan, "");
3093  }
3094  }
3095 
3096  if (!ast_test_flag64(confflags, (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
3097  if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED))
3098  if (!ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan)))
3099  ast_waitstream(chan, "");
3100  if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && conf->markedusers == 0)
3101  if (!ast_streamfile(chan, "conf-waitforleader", ast_channel_language(chan)))
3102  ast_waitstream(chan, "");
3103  }
3104 
3105  if (ast_test_flag64(confflags, CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
3106  int keepplaying = 1;
3107 
3108  if (conf->users == 2) {
3109  if (!ast_streamfile(chan, "conf-onlyone", ast_channel_language(chan))) {
3110  res = ast_waitstream(chan, AST_DIGIT_ANY);
3111  ast_stopstream(chan);
3112  if (res > 0)
3113  keepplaying = 0;
3114  else if (res == -1)
3115  goto outrun;
3116  }
3117  } else {
3118  if (!ast_streamfile(chan, "conf-thereare", ast_channel_language(chan))) {
3119  res = ast_waitstream(chan, AST_DIGIT_ANY);
3120  ast_stopstream(chan);
3121  if (res > 0)
3122  keepplaying = 0;
3123  else if (res == -1)
3124  goto outrun;
3125  }
3126  if (keepplaying) {
3127  res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
3128  if (res > 0)
3129  keepplaying = 0;
3130  else if (res == -1)
3131  goto outrun;
3132  }
3133  if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", ast_channel_language(chan))) {
3134  res = ast_waitstream(chan, AST_DIGIT_ANY);
3135  ast_stopstream(chan);
3136  if (res > 0)
3137  keepplaying = 0;
3138  else if (res == -1)
3139  goto outrun;
3140  }
3141  }
3142  }
3143 
3144  if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
3145  /* We're leaving this alone until the state gets changed to up */
3146  ast_indicate(chan, -1);
3147  }
3148 
3149  if (ast_set_write_format(chan, ast_format_slin) < 0) {
3150  ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", ast_channel_name(chan));
3151  goto outrun;
3152  }
3153 
3154  if (ast_set_read_format(chan, ast_format_slin) < 0) {
3155  ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", ast_channel_name(chan));
3156  goto outrun;
3157  }
3158 
3159  /* Reduce background noise from each participant */
3160  if (!ast_test_flag64(confflags, CONFFLAG_DONT_DENOISE)) {
3161  ast_func_write(chan, "DENOISE(rx)", "on");
3162  }
3163 
3164  retrydahdi = (strcasecmp(ast_channel_tech(chan)->type, "DAHDI") || (ast_channel_audiohooks(chan)) ? 1 : 0);
3165  user->dahdichannel = !retrydahdi;
3166 
3167  dahdiretry:
3168  origfd = ast_channel_fd(chan, 0);
3169  if (retrydahdi) {
3170  /* open pseudo in non-blocking mode */
3171  fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
3172  if (fd < 0) {
3173  ast_log(LOG_WARNING, "Unable to open DAHDI pseudo channel: %s\n", strerror(errno));
3174  goto outrun;
3175  }
3176  using_pseudo = 1;
3177  /* Setup buffering information */
3178  memset(&bi, 0, sizeof(bi));
3179  bi.bufsize = CONF_SIZE / 2;
3180  bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
3181  bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
3182  bi.numbufs = audio_buffers;
3183  if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
3184  ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
3185  close(fd);
3186  goto outrun;
3187  }
3188  x = 1;
3189  if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
3190  ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
3191  close(fd);
3192  goto outrun;
3193  }
3194  nfds = 1;
3195  } else {
3196  /* XXX Make sure we're not running on a pseudo channel XXX */
3197  fd = ast_channel_fd(chan, 0);
3198  nfds = 0;
3199  }
3200  memset(&dahdic, 0, sizeof(dahdic));
3201  memset(&dahdic_empty, 0, sizeof(dahdic_empty));
3202  /* Check to see if we're in a conference... */
3203  dahdic.chan = 0;
3204  if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
3205  ast_log(LOG_WARNING, "Error getting conference\n");
3206  close(fd);
3207  goto outrun;
3208  }
3209  if (dahdic.confmode) {
3210  /* Whoa, already in a conference... Retry... */
3211  if (!retrydahdi) {
3212  ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
3213  retrydahdi = 1;
3214  goto dahdiretry;
3215  }
3216  }
3217  memset(&dahdic, 0, sizeof(dahdic));
3218  /* Add us to the conference */
3219  dahdic.chan = 0;
3220  dahdic.confno = conf->dahdiconf;
3221 
3222  if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
3223  ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) || ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)) && conf->users > 1) {
3224  struct announce_listitem *item;
3225  if (!(item = ao2_alloc(sizeof(*item), NULL)))
3226  goto outrun;
3227  ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
3228  ast_copy_string(item->language, ast_channel_language(chan), sizeof(item->language));
3229  item->confchan = conf->chan;
3230  item->confusers = conf->users;
3231  if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
3232  item->vmrec = 1;
3233  }
3234  item->announcetype = CONF_HASJOIN;
3235  ast_mutex_lock(&conf->announcelistlock);
3236  ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
3237  AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
3238  ast_cond_signal(&conf->announcelist_addition);
3239  ast_mutex_unlock(&conf->announcelistlock);
3240 
3241  while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
3242  ;
3243  }
3244  ao2_ref(item, -1);
3245  }
3246 
3247  if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && !conf->markedusers)
3248  dahdic.confmode = DAHDI_CONF_CONF;
3249  else if (ast_test_flag64(confflags, CONFFLAG_MONITOR))
3250  dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
3251  else if (ast_test_flag64(confflags, CONFFLAG_TALKER))
3252  dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
3253  else
3254  dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
3255 
3256  if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3257  ast_log(LOG_WARNING, "Error setting conference\n");
3258  close(fd);
3259  goto outrun;
3260  }
3261  ast_debug(1, "Placed channel %s in DAHDI conf %d\n", ast_channel_name(chan), conf->dahdiconf);
3262 
3263  if (!sent_event) {
3264  meetme_stasis_generate_msg(conf, chan, user, meetme_join_type(), NULL);
3265  sent_event = 1;
3266  }
3267 
3268  if (!firstpass && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
3269  !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
3270  firstpass = 1;
3271  if (!ast_test_flag64(confflags, CONFFLAG_QUIET))
3272  if (!ast_test_flag64(confflags, CONFFLAG_WAITMARKED) || (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
3273  (conf->markedusers >= 1))) {
3274  conf_play(chan, conf, ENTER);
3275  }
3276  }
3277 
3278  conf_flush(fd, chan);
3279 
3280  if (dsp)
3281  ast_dsp_free(dsp);
3282 
3283  if (!(dsp = ast_dsp_new())) {
3284  ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
3285  res = -1;
3286  }
3287 
3288  if (ast_test_flag64(confflags, CONFFLAG_AGI)) {
3289  /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
3290  or use default filename of conf-background.agi */
3291 
3292  ast_channel_lock(chan);
3293  if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
3294  agifile = ast_strdupa(tmpvar);
3295  } else {
3296  agifile = ast_strdupa(agifiledefault);
3297  }
3298  ast_channel_unlock(chan);
3299 
3300  if (user->dahdichannel) {
3301  /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
3302  x = 1;
3303  ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
3304  }
3305  /* Find a pointer to the agi app and execute the script */
3306  agi_app = pbx_findapp("agi");
3307  if (agi_app) {
3308  ret = pbx_exec(chan, agi_app, agifile);
3309  } else {
3310  ast_log(LOG_WARNING, "Could not find application (agi)\n");
3311  ret = -2;
3312  }
3313  if (user->dahdichannel) {
3314  /* Remove CONFMUTE mode on DAHDI channel */
3315  x = 0;
3316  ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
3317  }
3318  } else {
3319  int lastusers = conf->users;
3320  if (user->dahdichannel && ast_test_flag64(confflags, CONFFLAG_STARMENU)) {
3321  /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
3322  x = 1;
3323  ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
3324  }
3325 
3326  for (;;) {
3327  int menu_was_active = 0;
3328 
3329  outfd = -1;
3330  ms = -1;
3331  now = ast_tvnow();
3332 
3333  if (rt_schedule && conf->endtime) {
3334  char currenttime[32];
3335  long localendtime = 0;
3336  int extended = 0;
3337  struct ast_tm tm;
3338  struct ast_variable *var, *origvar;
3339  struct timeval tmp;
3340 
3341  if (now.tv_sec % 60 == 0) {
3342  if (!checked) {
3343  ast_localtime(&now, &tm, NULL);
3344  ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
3345  var = origvar = ast_load_realtime("meetme", "confno",
3346  conf->confno, "starttime <=", currenttime,
3347  "endtime >=", currenttime, NULL);
3348 
3349  for ( ; var; var = var->next) {
3350  if (!strcasecmp(var->name, "endtime")) {
3351  struct ast_tm endtime_tm;
3352  ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
3353  tmp = ast_mktime(&endtime_tm, NULL);
3354  localendtime = tmp.tv_sec;
3355  }
3356  }
3357  ast_variables_destroy(origvar);
3358 
3359  /* A conference can be extended from the
3360  Admin/User menu or by an external source */
3361  if (localendtime > conf->endtime){
3362  conf->endtime = localendtime;
3363  extended = 1;
3364  }
3365 
3366  if (conf->endtime && (now.tv_sec >= conf->endtime)) {
3367  ast_verbose("Quitting time...\n");
3368  goto outrun;
3369  }
3370 
3371  if (!announcement_played && conf->endalert) {
3372  if (now.tv_sec + conf->endalert >= conf->endtime) {
3373  if (!ast_streamfile(chan, "conf-will-end-in", ast_channel_language(chan)))
3374  ast_waitstream(chan, "");
3375  ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", ast_channel_language(chan));
3376  if (!ast_streamfile(chan, "minutes", ast_channel_language(chan)))
3377  ast_waitstream(chan, "");
3378  if (musiconhold) {
3379  conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3380  }
3381  announcement_played = 1;
3382  }
3383  }
3384 
3385  if (extended) {
3386  announcement_played = 0;
3387  }
3388 
3389  checked = 1;
3390  }
3391  } else {
3392  checked = 0;
3393  }
3394  }
3395 
3396  if (user->kicktime && (user->kicktime <= now.tv_sec)) {
3397  if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
3398  ret = 0;
3399  } else {
3400  ret = -1;
3401  }
3402  break;
3403  }
3404 
3405  to = -1;
3406  if (user->timelimit) {
3407  int minutes = 0, seconds = 0, remain = 0;
3408 
3409  to = ast_tvdiff_ms(nexteventts, now);
3410  if (to < 0) {
3411  to = 0;
3412  }
3413  time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
3414  if (time_left_ms < to) {
3415  to = time_left_ms;
3416  }
3417 
3418  if (time_left_ms <= 0) {
3419  if (user->end_sound) {
3420  res = ast_streamfile(chan, user->end_sound, ast_channel_language(chan));
3421  res = ast_waitstream(chan, "");
3422  }
3423  if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
3424  ret = 0;
3425  } else {
3426  ret = -1;
3427  }
3428  break;
3429  }
3430 
3431  if (!to) {
3432  if (time_left_ms >= 5000) {
3433 
3434  remain = (time_left_ms + 500) / 1000;
3435  if (remain / 60 >= 1) {
3436  minutes = remain / 60;
3437  seconds = remain % 60;
3438  } else {
3439  seconds = remain;
3440  }
3441 
3442  /* force the time left to round up if appropriate */
3443  if (user->warning_sound && user->play_warning) {
3444  if (!strcmp(user->warning_sound, "timeleft")) {
3445 
3446  res = ast_streamfile(chan, "vm-youhave", ast_channel_language(chan));
3447  res = ast_waitstream(chan, "");
3448  if (minutes) {
3449  res = ast_say_number(chan, minutes, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
3450  res = ast_streamfile(chan, "queue-minutes", ast_channel_language(chan));
3451  res = ast_waitstream(chan, "");
3452  }
3453  if (seconds) {
3454  res = ast_say_number(chan, seconds, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
3455  res = ast_streamfile(chan, "queue-seconds", ast_channel_language(chan));
3456  res = ast_waitstream(chan, "");
3457  }
3458  } else {
3459  res = ast_streamfile(chan, user->warning_sound, ast_channel_language(chan));
3460  res = ast_waitstream(chan, "");
3461  }
3462  if (musiconhold) {
3463  conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3464  }
3465  }
3466  }
3467  if (user->warning_freq) {
3468  nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
3469  } else {
3470  nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
3471  }
3472  }
3473  }
3474 
3475  now = ast_tvnow();
3476  if (timeout && now.tv_sec >= timeout) {
3477  if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
3478  ret = 0;
3479  } else {
3480  ret = -1;
3481  }
3482  break;
3483  }
3484 
3485  /* if we have just exited from the menu, and the user had a channel-driver
3486  volume adjustment, restore it
3487  */
3488  if (!menu_mode && menu_was_active && user->listen.desired && !user->listen.actual) {
3489  set_talk_volume(user, user->listen.desired);
3490  }
3491 
3492  menu_was_active = menu_mode;
3493 
3494  currentmarked = conf->markedusers;
3495  if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
3496  ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
3497  ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
3498  lastmarked == 0) {
3499  if (currentmarked == 1 && conf->users > 1) {
3500  ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
3501  if (conf->users - 1 == 1) {
3502  if (!ast_streamfile(chan, "conf-userwilljoin", ast_channel_language(chan))) {
3503  ast_waitstream(chan, "");
3504  }
3505  } else {
3506  if (!ast_streamfile(chan, "conf-userswilljoin", ast_channel_language(chan))) {
3507  ast_waitstream(chan, "");
3508  }
3509  }
3510  }
3511  if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
3512  if (!ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan))) {
3513  ast_waitstream(chan, "");
3514  }
3515  }
3516  }
3517 
3518  /* Update the struct with the actual confflags */
3519  user->userflags = *confflags;
3520 
3521  if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
3522  if (currentmarked == 0) {
3523  if (lastmarked != 0) {
3524  if (!ast_test_flag64(confflags, CONFFLAG_QUIET)) {
3525  if (!ast_streamfile(chan, "conf-leaderhasleft", ast_channel_language(chan))) {
3526  ast_waitstream(chan, "");
3527  }
3528  }
3529  if (ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
3530  if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
3531  ret = 0;
3532  }
3533  break;
3534  } else {
3535  dahdic.confmode = DAHDI_CONF_CONF;
3536  if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3537  ast_log(LOG_WARNING, "Error setting conference\n");
3538  close(fd);
3539  goto outrun;
3540  }
3541  }
3542  }
3543  if (!musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
3544  conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3545  musiconhold = 1;
3546  }
3547  } else if (currentmarked >= 1 && lastmarked == 0) {
3548  /* Marked user entered, so cancel timeout */
3549  timeout = 0;
3550  if (ast_test_flag64(confflags, CONFFLAG_MONITOR)) {
3551  dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
3552  } else if (ast_test_flag64(confflags, CONFFLAG_TALKER)) {
3553  dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
3554  } else {
3555  dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
3556  }
3557  if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3558  ast_log(LOG_WARNING, "Error setting conference\n");
3559  close(fd);
3560  goto outrun;
3561  }
3562  if (musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
3563  ast_moh_stop(chan);
3564  musiconhold = 0;
3565  }
3566  if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
3567  !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
3568  if (!ast_streamfile(chan, "conf-placeintoconf", ast_channel_language(chan))) {
3569  ast_waitstream(chan, "");
3570  }
3571  conf_play(chan, conf, ENTER);
3572  }
3573  }
3574  }
3575 
3576  /* trying to add moh for single person conf */
3577  if (ast_test_flag64(confflags, CONFFLAG_MOH) && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
3578  if (conf->users == 1) {
3579  if (!musiconhold) {
3580  conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3581  musiconhold = 1;
3582  }
3583  } else {
3584  if (musiconhold) {
3585  ast_moh_stop(chan);
3586  musiconhold = 0;
3587  }
3588  }
3589  }
3590 
3591  /* Leave if the last marked user left */
3592  if (currentmarked == 0 && lastmarked != 0 && ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
3593  if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
3594  ret = 0;
3595  } else {
3596  ret = -1;
3597  }
3598  break;
3599  }
3600 
3601  /* Throw a TestEvent if a user exit did not cause this user to leave the conference */
3602  if (conf->users != lastusers) {
3603  if (conf->users < lastusers) {
3604  ast_test_suite_event_notify("NOEXIT", "Message: CONFFLAG_MARKEDEXIT\r\nLastUsers: %d\r\nUsers: %d", lastusers, conf->users);
3605  }
3606  lastusers = conf->users;
3607  }
3608 
3609  /* Check if my modes have changed */
3610 
3611  /* If I should be muted but am still talker, mute me */
3612  if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
3613  RAII_VAR(struct ast_json *, status_blob, status_to_json(1), ast_json_unref);
3614  dahdic.confmode ^= DAHDI_CONF_TALKER;
3615  if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3616  ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
3617  ret = -1;
3618  break;
3619  }
3620 
3621  /* Indicate user is not talking anymore - change him to unmonitored state */
3622  if (ast_test_flag64(confflags, (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER))) {
3623  set_user_talking(chan, conf, user, -1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
3624  }
3625  meetme_stasis_generate_msg(conf, chan, user, meetme_mute_type(), status_blob);
3626  }
3627 
3628  /* If I should be un-muted but am not talker, un-mute me */
3629  if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
3630  RAII_VAR(struct ast_json *, status_blob, status_to_json(0), ast_json_unref);
3631  dahdic.confmode |= DAHDI_CONF_TALKER;
3632  if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3633  ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
3634  ret = -1;
3635  break;
3636  }
3637  meetme_stasis_generate_msg(conf, chan, user, meetme_mute_type(), status_blob);
3638  }
3639 
3640  if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
3641  (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
3642 
3643  RAII_VAR(struct ast_json *, status_blob, status_to_json(1), ast_json_unref);
3644  talkreq_manager = 1;
3645  meetme_stasis_generate_msg(conf, chan, user, meetme_talk_request_type(), status_blob);
3646  }
3647 
3648  if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
3649  !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
3650  RAII_VAR(struct ast_json *, status_blob, status_to_json(0), ast_json_unref);
3651  talkreq_manager = 0;
3652  meetme_stasis_generate_msg(conf, chan, user, meetme_talk_request_type(), status_blob);
3653  }
3654 
3655  /* If user have been hung up, exit the conference */
3656  if (user->adminflags & ADMINFLAG_HANGUP) {
3657  ret = 0;
3658  break;
3659  }
3660 
3661  /* If I have been kicked, exit the conference */
3662  if (user->adminflags & ADMINFLAG_KICKME) {
3663  /* You have been kicked. */
3664  if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
3665  !ast_streamfile(chan, "conf-kicked", ast_channel_language(chan))) {
3666  ast_waitstream(chan, "");
3667  }
3668  ret = 0;
3669  break;
3670  }
3671 
3672  /* Perform a hangup check here since ast_waitfor_nandfds will not always be able to get a channel after a hangup has occurred */
3673  if (ast_check_hangup(chan)) {
3674  break;
3675  }
3676 
3677  c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
3678 
3679  if (c) {
3680  char dtmfstr[2] = "";
3681 
3682  if (ast_channel_fd(c, 0) != origfd || (user->dahdichannel && ast_channel_audiohooks(c))) {
3683  if (using_pseudo) {
3684  /* Kill old pseudo */
3685  close(fd);
3686  using_pseudo = 0;
3687  }
3688  ast_debug(1, "Ooh, something swapped out under us, starting over\n");
3689  retrydahdi = (strcasecmp(ast_channel_tech(c)->type, "DAHDI") || ast_channel_audiohooks(c) ? 1 : 0);
3690  user->dahdichannel = !retrydahdi;
3691  goto dahdiretry;
3692  }
3693  if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
3694  f = ast_read_noaudio(c);
3695  } else {
3696  f = ast_read(c);
3697  }
3698  if (!f) {
3699  break;
3700  }
3701  if (f->frametype == AST_FRAME_DTMF) {
3702  dtmfstr[0] = f->subclass.integer;
3703  dtmfstr[1] = '\0';
3704  }
3705 
3707  if (user->talk.actual) {
3708  ast_frame_adjust_volume(f, user->talk.actual);
3709  }
3710 
3711  if (ast_test_flag64(confflags, (CONFFLAG_OPTIMIZETALKER | CONFFLAG_MONITORTALKER))) {
3712  if (user->talking == -1) {
3713  user->talking = 0;
3714  }
3715 
3716  res = ast_dsp_silence(dsp, f, &totalsilence);
3717  if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
3718  set_user_talking(chan, conf, user, 1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
3719  }
3720 
3721  if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
3722  set_user_talking(chan, conf, user, 0, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
3723  }
3724  }
3725  if (using_pseudo) {
3726  /* Absolutely do _not_ use careful_write here...
3727  it is important that we read data from the channel
3728  as fast as it arrives, and feed it into the conference.
3729  The buffering in the pseudo channel will take care of any
3730  timing differences, unless they are so drastic as to lose
3731  audio frames (in which case carefully writing would only
3732  have delayed the audio even further).
3733  */
3734  /* As it turns out, we do want to use careful write. We just
3735  don't want to block, but we do want to at least *try*
3736  to write out all the samples.
3737  */
3738  if (user->talking || !ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER)) {
3739  careful_write(fd, f->data.ptr, f->datalen, 0);
3740  }
3741  }
3742  } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass.integer == '*') && ast_test_flag64(confflags, CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_mode)) {
3743  if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
3744  conf_queue_dtmf(conf, user, f);
3745  }
3746  /* Take out of conference */
3747  if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
3748  ast_log(LOG_WARNING, "Error setting conference\n");
3749  close(fd);
3750  ast_frfree(f);
3751  goto outrun;
3752  }
3753 
3754  /* if we are entering the menu, and the user has a channel-driver
3755  volume adjustment, clear it
3756  */
3757  if (!menu_mode && user->talk.desired && !user->talk.actual) {
3758  set_talk_volume(user, 0);
3759  }
3760 
3761  if (musiconhold) {
3762  ast_moh_stop(chan);
3763  } else if (!menu_mode) {
3764  char *menu_to_play;
3765  if (ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
3766  menu_mode = MENU_ADMIN;
3767  menu_to_play = "conf-adminmenu-18";
3768  } else {
3769  menu_mode = MENU_NORMAL;
3770  menu_to_play = "conf-usermenu-162";
3771  }
3772 
3773  if (!ast_streamfile(chan, menu_to_play, ast_channel_language(chan))) {
3774  dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
3775  ast_stopstream(chan);
3776  } else {
3777  dtmf = 0;
3778  }
3779  } else {
3780  dtmf = f->subclass.integer;
3781  }
3782 
3783  if (dtmf > 0) {
3784  meetme_menu(&menu_mode, &dtmf, conf, confflags,
3785  chan, user, recordingtmp, sizeof(recordingtmp), cap_slin);
3786  }
3787 
3788  if (musiconhold && !menu_mode) {
3789  conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3790  }
3791 
3792  /* Put back into conference */
3793  if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3794  ast_log(LOG_WARNING, "Error setting conference\n");
3795  close(fd);
3796  ast_frfree(f);
3797  goto outrun;
3798  }
3799 
3800  conf_flush(fd, chan);
3801  /*
3802  * Since options using DTMF could absorb DTMF meant for the
3803  * conference menu, we have to check them after the menu.
3804  */
3805  } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
3806  if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
3807  conf_queue_dtmf(conf, user, f);
3808  }
3809 
3810  if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
3811  ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
3812  ret = 0;
3813  ast_frfree(f);
3814  break;
3815  } else {
3816  ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
3817  }
3818  } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_KEYEXIT) &&
3819  (strchr(exitkeys, f->subclass.integer))) {
3820  pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);
3821 
3822  if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
3823  conf_queue_dtmf(conf, user, f);
3824  }
3825  ret = 0;
3826  ast_frfree(f);
3827  break;
3828  } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
3829  && ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
3830  conf_queue_dtmf(conf, user, f);
3831  } else if (f->frametype == AST_FRAME_NULL) {
3832  /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
3833  } else if (f->frametype == AST_FRAME_CONTROL) {
3834  switch (f->subclass.integer) {
3835  case AST_CONTROL_BUSY:
3837  ast_frfree(f);
3838  goto outrun;
3839  break;
3840  default:
3841  ast_debug(1,
3842  "Got ignored control frame on channel %s, f->frametype=%u,f->subclass=%d\n",
3843  ast_channel_name(chan), f->frametype, f->subclass.integer);
3844  }
3845  } else {
3846  ast_debug(1,
3847  "Got unrecognized frame on channel %s, f->frametype=%u,f->subclass=%d\n",
3848  ast_channel_name(chan), f->frametype, f->subclass.integer);
3849  }
3850  ast_frfree(f);
3851  } else if (outfd > -1) {
3852  res = read(outfd, buf, CONF_SIZE);
3853  if (res > 0) {
3854  memset(&fr, 0, sizeof(fr));
3855  fr.frametype = AST_FRAME_VOICE;
3856  fr.subclass.format = ast_format_slin;
3857  fr.datalen = res;
3858  fr.samples = res / 2;
3859  fr.data.ptr = buf;
3860  fr.offset = AST_FRIENDLY_OFFSET;
3861  if (!user->listen.actual &&
3862  (ast_test_flag64(confflags, CONFFLAG_MONITOR) ||
3864  (!user->talking && ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER))
3865  )) {
3866  int idx;
3867  for (idx = 0; idx < AST_FRAME_BITS; idx++) {
3868  if (ast_format_compatibility_format2bitfield(ast_channel_rawwriteformat(chan)) & (1 << idx)) {
3869  break;
3870  }
3871  }
3872  if (idx >= AST_FRAME_BITS) {
3873  goto bailoutandtrynormal;
3874  }
3875  ast_mutex_lock(&conf->listenlock);
3876  if (!conf->transframe[idx]) {
3877  if (conf->origframe) {
3878  if (musiconhold
3879  && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)
3880  && !ast_dsp_silence(dsp, conf->origframe, &confsilence)
3881  && confsilence < MEETME_DELAYDETECTTALK) {
3882  ast_moh_stop(chan);
3883  mohtempstopped = 1;
3884  }
3885  if (!conf->transpath[idx]) {
3886  conf->transpath[idx] = ast_translator_build_path(ast_channel_rawwriteformat(chan), ast_format_slin);
3887  }
3888  if (conf->transpath[idx]) {
3889  conf->transframe[idx] = ast_translate(conf->transpath[idx], conf->origframe, 0);
3890  if (!conf->transframe[idx]) {
3891  conf->transframe[idx] = &ast_null_frame;
3892  }
3893  }
3894  }
3895  }
3896  if (conf->transframe[idx]) {
3897  if ((conf->transframe[idx]->frametype != AST_FRAME_NULL) &&
3898  can_write(chan, confflags)) {
3899  struct ast_frame *cur;
3900  /* the translator may have returned a list of frames, so
3901  write each one onto the channel
3902  */
3903  for (cur = conf->transframe[idx]; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
3904  if (ast_write(chan, cur)) {
3905  ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", ast_channel_name(chan));
3906  break;
3907  }
3908  }
3909  if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
3910  mohtempstopped = 0;
3911  conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3912  }
3913  }
3914  } else {
3915  ast_mutex_unlock(&conf->listenlock);
3916  goto bailoutandtrynormal;
3917  }
3918  ast_mutex_unlock(&conf->listenlock);
3919  } else {
3920 bailoutandtrynormal:
3921  if (musiconhold
3922  && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)
3923  && !ast_dsp_silence(dsp, &fr, &confsilence)
3924  && confsilence < MEETME_DELAYDETECTTALK) {
3925  ast_moh_stop(chan);
3926  mohtempstopped = 1;
3927  }
3928  if (user->listen.actual) {
3929  ast_frame_adjust_volume(&fr, user->listen.actual);
3930  }
3931  if (can_write(chan, confflags) && ast_write(chan, &fr) < 0) {
3932  ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", ast_channel_name(chan));
3933  }
3934  if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
3935  mohtempstopped = 0;
3936  conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3937  }
3938  }
3939  } else {
3940  ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
3941  }
3942  }
3943  lastmarked = currentmarked;
3944  }
3945  }
3946 
3947  if (musiconhold) {
3948  ast_moh_stop(chan);
3949  }
3950 
3951  if (using_pseudo) {
3952  close(fd);
3953  } else {
3954  /* Take out of conference */
3955  dahdic.chan = 0;
3956  dahdic.confno = 0;
3957  dahdic.confmode = 0;
3958  if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3959  ast_log(LOG_WARNING, "Error setting conference\n");
3960  }
3961  }
3962 
3963  reset_volumes(user);
3964 
3965  if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
3966  !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
3967  conf_play(chan, conf, LEAVE);
3968  }
3969 
3970  if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_INTROUSER |CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC) && conf->users > 1) {
3971  struct announce_listitem *item;
3972  if (!(item = ao2_alloc(sizeof(*item), NULL)))
3973  goto outrun;
3974  ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
3975  ast_copy_string(item->language, ast_channel_language(chan), sizeof(item->language));
3976  item->confchan = conf->chan;
3977  item->confusers = conf->users;
3978  item->announcetype = CONF_HASLEFT;
3979  if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
3980  item->vmrec = 1;
3981  }
3982  ast_mutex_lock(&conf->announcelistlock);
3983  AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
3984  ast_cond_signal(&conf->announcelist_addition);
3985  ast_mutex_unlock(&conf->announcelistlock);
3986  } 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) {
3987  /* Last person is leaving, so no reason to try and announce, but should delete the name recording */
3988  ast_filedelete(user->namerecloc, NULL);
3989  }
3990 
3991  outrun:
3992  AST_LIST_LOCK(&confs);
3993 
3994  if (dsp) {
3995  ast_dsp_free(dsp);
3996  }
3997 
3998  if (user->user_no) {
3999  /* Only cleanup users who really joined! */
4000  now = ast_tvnow();
4001 
4002  if (sent_event) {
4003  meetme_stasis_generate_msg(conf, chan, user, meetme_leave_type(), NULL);
4004  }
4005 
4006  if (setusercount) {
4007  conf->users--;
4008  if (rt_log_members) {
4009  /* Update table */
4010  snprintf(members, sizeof(members), "%d", conf->users);
4011  ast_realtime_require_field("meetme",
4012  "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
4013  "members", RQ_UINTEGER1, strlen(members),
4014  NULL);
4015  ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
4016  }
4017  if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
4018  conf->markedusers--;
4019  }
4020  }
4021  /* Remove ourselves from the container */
4022  ao2_unlink(conf->usercontainer, user);
4023 
4024  /* Change any states */
4025  if (!conf->users) {
4027  }
4028 
4029  /* This flag is meant to kill a conference with only one participant remaining. */
4030  if (conf->users == 1 && ast_test_flag64(confflags, CONFFLAG_KILL_LAST_MAN_STANDING)) {
4031  ao2_callback(conf->usercontainer, 0, user_set_hangup_cb, NULL);
4032  }
4033 
4034  /* Return the number of seconds the user was in the conf */
4035  snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
4036  pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
4037 
4038  /* Return the RealTime bookid for CDR linking */
4039  if (rt_schedule) {
4040  pbx_builtin_setvar_helper(chan, "MEETMEBOOKID", conf->bookid);
4041  }
4042  }
4043  ao2_ref(user, -1);
4045 
4046 
4047 conf_run_cleanup:
4048  ao2_cleanup(cap_slin);
4049 
4050  return ret;
4051 }
4052 
4053 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
4054  char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags64 *confflags, int *too_early, char **optargs)
4055 {
4056  struct ast_variable *var, *origvar;
4057  struct ast_conference *cnf;
4058 
4059  *too_early = 0;
4060 
4061  /* Check first in the conference list */
4062  AST_LIST_LOCK(&confs);
4063  AST_LIST_TRAVERSE(&confs, cnf, list) {
4064  if (!strcmp(confno, cnf->confno)) {
4065  break;
4066  }
4067  }
4068  if (cnf) {
4069  cnf->refcount += refcount;
4070  }
4072 
4073  if (!cnf) {
4074  char *pin = NULL, *pinadmin = NULL; /* For temp use */
4075  int maxusers = 0;
4076  struct timeval now;
4077  char recordingfilename[256] = "";
4078  char recordingformat[11] = "";
4079  char currenttime[32] = "";
4080  char eatime[32] = "";
4081  char bookid[51] = "";
4082  char recordingtmp[AST_MAX_EXTENSION * 2] = "";
4083  char useropts[OPTIONS_LEN + 1] = ""; /* Used for RealTime conferences */
4084  char adminopts[OPTIONS_LEN + 1] = "";
4085  struct ast_tm tm, etm;
4086  struct timeval endtime = { .tv_sec = 0 };
4087  const char *var2;
4088 
4089  if (rt_schedule) {
4090  now = ast_tvnow();
4091 
4092  ast_localtime(&now, &tm, NULL);
4093  ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
4094 
4095  ast_debug(1, "Looking for conference %s that starts after %s\n", confno, currenttime);
4096 
4097  var = ast_load_realtime("meetme", "confno",
4098  confno, "starttime <= ", currenttime, "endtime >= ",
4099  currenttime, NULL);
4100 
4101  if (!var && fuzzystart) {
4102  now = ast_tvnow();
4103  now.tv_sec += fuzzystart;
4104 
4105  ast_localtime(&now, &tm, NULL);
4106  ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
4107  var = ast_load_realtime("meetme", "confno",
4108  confno, "starttime <= ", currenttime, "endtime >= ",
4109  currenttime, NULL);
4110  }
4111 
4112  if (!var && earlyalert) {
4113  now = ast_tvnow();
4114  now.tv_sec += earlyalert;
4115  ast_localtime(&now, &etm, NULL);
4116  ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
4117  var = ast_load_realtime("meetme", "confno",
4118  confno, "starttime <= ", eatime, "endtime >= ",
4119  currenttime, NULL);
4120  if (var) {
4121  *too_early = 1;
4122  }
4123  }
4124 
4125  } else {
4126  var = ast_load_realtime("meetme", "confno", confno, NULL);
4127  }
4128 
4129  if (!var) {
4130  return NULL;
4131  }
4132 
4133  if (rt_schedule && *too_early) {
4134  /* Announce that the caller is early and exit */
4135  if (!ast_streamfile(chan, "conf-has-not-started", ast_channel_language(chan))) {
4136  ast_waitstream(chan, "");
4137  }
4138  ast_variables_destroy(var);
4139  return NULL;
4140  }
4141 
4142  for (origvar = var; var; var = var->next) {
4143  if (!strcasecmp(var->name, "pin")) {
4144  pin = ast_strdupa(var->value);
4145  } else if (!strcasecmp(var->name, "adminpin")) {
4146  pinadmin = ast_strdupa(var->value);
4147  } else if (!strcasecmp(var->name, "bookId")) {
4148  ast_copy_string(bookid, var->value, sizeof(bookid));
4149  } else if (!strcasecmp(var->name, "opts")) {
4150  ast_copy_string(useropts, var->value, sizeof(char[OPTIONS_LEN + 1]));
4151  } else if (!strcasecmp(var->name, "maxusers")) {
4152  maxusers = atoi(var->value);
4153  } else if (!strcasecmp(var->name, "adminopts")) {
4154  ast_copy_string(adminopts, var->value, sizeof(char[OPTIONS_LEN + 1]));
4155  } else if (!strcasecmp(var->name, "recordingfilename")) {
4156  ast_copy_string(recordingfilename, var->value, sizeof(recordingfilename));
4157  } else if (!strcasecmp(var->name, "recordingformat")) {
4158  ast_copy_string(recordingformat, var->value, sizeof(recordingformat));
4159  } else if (!strcasecmp(var->name, "endtime")) {
4160  struct ast_tm endtime_tm;
4161  ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
4162  endtime = ast_mktime(&endtime_tm, NULL);
4163  }
4164  }
4165 
4166  ast_variables_destroy(origvar);
4167 
4168  cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan, NULL);
4169 
4170  if (cnf) {
4171  struct ast_flags64 tmp_flags;
4172 
4173  cnf->maxusers = maxusers;
4174  cnf->endalert = endalert;
4175  cnf->endtime = endtime.tv_sec;
4176  cnf->useropts = ast_strdup(useropts);
4177  cnf->adminopts = ast_strdup(adminopts);
4178  cnf->bookid = ast_strdup(bookid);
4179  if (!ast_strlen_zero(recordingfilename)) {
4180  cnf->recordingfilename = ast_strdup(recordingfilename);
4181  }
4182  if (!ast_strlen_zero(recordingformat)) {
4183  cnf->recordingformat = ast_strdup(recordingformat);
4184  }
4185 
4186  /* Parse the other options into confflags -- need to do this in two
4187  * steps, because the parse_options routine zeroes the buffer. */
4188  ast_app_parse_options64(meetme_opts, &tmp_flags, optargs, useropts);
4189  ast_copy_flags64(confflags, &tmp_flags, tmp_flags.flags);
4190 
4191  if (strchr(cnf->useropts, 'r')) {
4192  if (ast_strlen_zero(recordingfilename)) { /* If the recordingfilename in the database is empty, use the channel definition or use the default. */
4193  ast_channel_lock(chan);
4194  if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
4195  ast_free(cnf->recordingfilename);
4196  cnf->recordingfilename = ast_strdup(var2);
4197  }
4198  ast_channel_unlock(chan);
4199  if (ast_strlen_zero(cnf->recordingfilename)) {
4200  snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", cnf->confno, ast_channel_uniqueid(chan));
4201  ast_free(cnf->recordingfilename);
4202  cnf->recordingfilename = ast_strdup(recordingtmp);
4203  }
4204  }
4205  if (ast_strlen_zero(cnf->recordingformat)) {/* If the recording format is empty, use the wav as default */
4206  ast_channel_lock(chan);
4207  if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
4208  ast_free(cnf->recordingformat);
4209  cnf->recordingformat = ast_strdup(var2);
4210  }
4211  ast_channel_unlock(chan);
4212  if (ast_strlen_zero(cnf->recordingformat)) {
4213  ast_free(cnf->recordingformat);
4214  cnf->recordingformat = ast_strdup("wav");
4215  }
4216  }
4217  ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
4218  }
4219  }
4220  }
4221 
4222  if (cnf) {
4223  if (confflags->flags && !cnf->chan &&
4224  !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
4225  ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC)) {
4226  ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
4227  ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC);
4228  }
4229 
4230  if (confflags && !cnf->chan &&
4231  ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
4232  ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
4233  ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
4234  }
4235  }
4236 
4237  return cnf;
4238 }
4239 
4240 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
4241  char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags64 *confflags)
4242 {
4243  struct ast_config *cfg;
4244  struct ast_variable *var;
4245  struct ast_flags config_flags = { 0 };
4246  struct ast_conference *cnf;
4247 
4248  AST_DECLARE_APP_ARGS(args,
4249  AST_APP_ARG(confno);
4250  AST_APP_ARG(pin);
4251  AST_APP_ARG(pinadmin);
4252  );
4253 
4254  /* Check first in the conference list */
4255  ast_debug(1, "The requested confno is '%s'?\n", confno);
4256  AST_LIST_LOCK(&confs);
4257  AST_LIST_TRAVERSE(&confs, cnf, list) {
4258  ast_debug(3, "Does conf %s match %s?\n", confno, cnf->confno);
4259  if (!strcmp(confno, cnf->confno))
4260  break;
4261  }
4262  if (cnf) {
4263  cnf->refcount += refcount;
4264  }
4266 
4267  if (!cnf) {
4268  if (dynamic) {
4269  /* No need to parse meetme.conf */
4270  ast_debug(1, "Building dynamic conference '%s'\n", confno);
4271  if (dynamic_pin) {
4272  if (dynamic_pin[0] == 'q') {
4273  /* Query the user to enter a PIN */
4274  if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
4275  return NULL;
4276  }
4277  cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan, NULL);
4278  } else {
4279  cnf = build_conf(confno, "", "", make, dynamic, refcount, chan, NULL);
4280  }
4281  } else {
4282  /* Check the config */
4283  cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
4284  if (!cfg) {
4285  ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
4286  return NULL;
4287  } else if (cfg == CONFIG_STATUS_FILEINVALID) {
4288  ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format. Aborting.\n");
4289  return NULL;
4290  }
4291 
4292  for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
4293  char parse[MAX_SETTINGS];
4294 
4295  if (strcasecmp(var->name, "conf"))
4296  continue;
4297 
4298  ast_copy_string(parse, var->value, sizeof(parse));
4299 
4300  AST_STANDARD_APP_ARGS(args, parse);
4301  ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
4302  if (!strcasecmp(args.confno, confno)) {
4303  /* Bingo it's a valid conference */
4304  cnf = build_conf(args.confno,
4305  S_OR(args.pin, ""),
4306  S_OR(args.pinadmin, ""),
4307  make, dynamic, refcount, chan, NULL);
4308  break;
4309  }
4310  }
4311  if (!var) {
4312  ast_log(LOG_WARNING, "%s isn't a valid conference\n", confno);
4313  }
4314  ast_config_destroy(cfg);
4315  }
4316  } else if (dynamic_pin) {
4317  /* Correct for the user selecting 'D' instead of 'd' to have
4318  someone join into a conference that has already been created
4319  with a pin. */
4320  if (dynamic_pin[0] == 'q') {
4321  dynamic_pin[0] = '\0';
4322  }
4323  }
4324 
4325  if (cnf) {
4326  if (confflags && !cnf->chan &&
4327  !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
4328  ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC)) {
4329  ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
4330  ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC);
4331  }
4332 
4333  if (confflags && !cnf->chan &&
4334  ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
4335  ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
4336  ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
4337  }
4338  }
4339 
4340  return cnf;
4341 }
4342 
4343 /*! \brief The MeetmeCount application */
4344 static int count_exec(struct ast_channel *chan, const char *data)
4345 {
4346  int res = 0;
4347  struct ast_conference *conf;
4348  int count;
4349  char *localdata;
4350  char val[80] = "0";
4351  AST_DECLARE_APP_ARGS(args,
4352  AST_APP_ARG(confno);
4353  AST_APP_ARG(varname);
4354  );
4355 
4356  if (ast_strlen_zero(data)) {
4357  ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
4358  return -1;
4359  }
4360 
4361  localdata = ast_strdupa(data);
4362 
4363  AST_STANDARD_APP_ARGS(args, localdata);
4364 
4365  conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
4366 
4367  if (conf) {
4368  count = conf->users;
4369  dispose_conf(conf);
4370  conf = NULL;
4371  } else
4372  count = 0;
4373 
4374  if (!ast_strlen_zero(args.varname)) {
4375  /* have var so load it and exit */
4376  snprintf(val, sizeof(val), "%d", count);
4377  pbx_builtin_setvar_helper(chan, args.varname, val);
4378  } else {
4379  if (ast_channel_state(chan) != AST_STATE_UP) {
4380  ast_answer(chan);
4381  }
4382  res = ast_say_number(chan, count, "", ast_channel_language(chan), (char *) NULL); /* Needs gender */
4383  }
4384 
4385  return res;
4386 }
4387 
4388 /*! \brief The meetme() application */
4389 static int conf_exec(struct ast_channel *chan, const char *data)
4390 {
4391  int res = -1;
4392  char confno[MAX_CONFNUM] = "";
4393  int allowretry = 0;
4394  int retrycnt = 0;
4395  struct ast_conference *cnf = NULL;
4396  struct ast_flags64 confflags = {0};
4397  struct ast_flags config_flags = { 0 };
4398  int dynamic = 0;
4399  int empty = 0, empty_no_pin = 0;
4400  int always_prompt = 0;
4401  const char *notdata;
4402  char *info, the_pin[MAX_PIN] = "";
4403  AST_DECLARE_APP_ARGS(args,
4404  AST_APP_ARG(confno);
4405  AST_APP_ARG(options);
4406  AST_APP_ARG(pin);
4407  );
4408  char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
4409 
4410  if (ast_strlen_zero(data)) {
4411  allowretry = 1;
4412  notdata = "";
4413  } else {
4414  notdata = data;
4415  }
4416 
4417  if (ast_channel_state(chan) != AST_STATE_UP)
4418  ast_answer(chan);
4419 
4420  info = ast_strdupa(notdata);
4421 
4422  AST_STANDARD_APP_ARGS(args, info);
4423 
4424  if (args.confno) {
4425  ast_copy_string(confno, args.confno, sizeof(confno));
4426  if (ast_strlen_zero(confno)) {
4427  allowretry = 1;
4428  }
4429  }
4430 
4431  if (args.pin)
4432  ast_copy_string(the_pin, args.pin, sizeof(the_pin));
4433 
4434  if (args.options) {
4435  ast_app_parse_options64(meetme_opts, &confflags, optargs, args.options);
4436  dynamic = ast_test_flag64(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
4437  if (ast_test_flag64(&confflags, CONFFLAG_DYNAMICPIN) && ast_strlen_zero(args.pin))
4438  strcpy(the_pin, "q");
4439 
4440  empty = ast_test_flag64(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
4441  empty_no_pin = ast_test_flag64(&confflags, CONFFLAG_EMPTYNOPIN);
4442  always_prompt = ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT | CONFFLAG_DYNAMICPIN);
4443  }
4444 
4445  do {
4446  if (retrycnt > 3)
4447  allowretry = 0;
4448  if (empty) {
4449  int i;
4450  struct ast_config *cfg;
4451  struct ast_variable *var;
4452  int confno_int;
4453 
4454  /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
4455  if ((empty_no_pin) || (!dynamic)) {
4456  cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
4457  if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
4458  var = ast_variable_browse(cfg, "rooms");
4459  while (var) {
4460  char parse[MAX_SETTINGS], *stringp = parse, *confno_tmp;
4461  if (!strcasecmp(var->name, "conf")) {
4462  int found = 0;
4463  ast_copy_string(parse, var->value, sizeof(parse));
4464  confno_tmp = strsep(&stringp, "|,");
4465  if (!dynamic) {
4466  /* For static: run through the list and see if this conference is empty */
4467  AST_LIST_LOCK(&confs);
4468  AST_LIST_TRAVERSE(&confs, cnf, list) {
4469  if (!strcmp(confno_tmp, cnf->confno)) {
4470  /* The conference exists, therefore it's not empty */
4471  found = 1;
4472  break;
4473  }
4474  }
4476  cnf = NULL;
4477  if (!found) {
4478  /* At this point, we have a confno_tmp (static conference) that is empty */
4479  if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
4480  /* Case 1: empty_no_pin and pin is nonexistent (NULL)
4481  * Case 2: empty_no_pin and pin is blank (but not NULL)
4482  * Case 3: not empty_no_pin
4483  */
4484  ast_copy_string(confno, confno_tmp, sizeof(confno));
4485  break;
4486  }
4487  }
4488  }
4489  }
4490  var = var->next;
4491  }
4492  ast_config_destroy(cfg);
4493  }
4494 
4495  if (ast_strlen_zero(confno) && (cfg = ast_load_realtime_multientry("meetme", "confno LIKE", "%", SENTINEL))) {
4496  const char *catg;
4497  for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
4498  const char *confno_tmp = ast_variable_retrieve(cfg, catg, "confno");
4499  const char *pin_tmp = ast_variable_retrieve(cfg, catg, "pin");
4500  if (ast_strlen_zero(confno_tmp)) {
4501  continue;
4502  }
4503  if (!dynamic) {
4504  int found = 0;
4505  /* For static: run through the list and see if this conference is empty */
4506  AST_LIST_LOCK(&confs);
4507  AST_LIST_TRAVERSE(&confs, cnf, list) {
4508  if (!strcmp(confno_tmp, cnf->confno)) {
4509  /* The conference exists, therefore it's not empty */
4510  found = 1;
4511  break;
4512  }
4513  }
4515  if (!found) {
4516  /* At this point, we have a confno_tmp (realtime conference) that is empty */
4517  if ((empty_no_pin && ast_strlen_zero(pin_tmp)) || (!empty_no_pin)) {
4518  /* Case 1: empty_no_pin and pin is nonexistent (NULL)
4519  * Case 2: empty_no_pin and pin is blank (but not NULL)
4520  * Case 3: not empty_no_pin
4521  */
4522  ast_copy_string(confno, confno_tmp, sizeof(confno));
4523  break;
4524  }
4525  }
4526  }
4527  }
4528  ast_config_destroy(cfg);
4529  }
4530  }
4531 
4532  /* Select first conference number not in use */
4533  if (ast_strlen_zero(confno) && dynamic) {
4534  AST_LIST_LOCK(&confs);
4535  for (i = 0; i < ARRAY_LEN(conf_map); i++) {
4536  if (!conf_map[i]) {
4537  snprintf(confno, sizeof(confno), "%d", i);
4538  conf_map[i] = 1;
4539  break;
4540  }
4541  }
4543  }
4544 
4545  /* Not found? */
4546  if (ast_strlen_zero(confno)) {
4547  res = ast_streamfile(chan, "conf-noempty", ast_channel_language(chan));
4548  ast_test_suite_event_notify("PLAYBACK", "Message: conf-noempty");
4549  if (!res)
4550  ast_waitstream(chan, "");
4551  } else {
4552  if (sscanf(confno, "%30d", &confno_int) == 1) {
4553  if (!ast_test_flag64(&confflags, CONFFLAG_QUIET)) {
4554  res = ast_streamfile(chan, "conf-enteringno", ast_channel_language(chan));
4555  if (!res) {
4556  ast_waitstream(chan, "");
4557  res = ast_say_digits(chan, confno_int, "", ast_channel_language(chan));
4558  }
4559  }
4560  } else {
4561  ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
4562  }
4563  }
4564  }
4565 
4566  while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
4567  /* Prompt user for conference number */
4568  res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
4569  if (res < 0) {
4570  /* Don't try to validate when we catch an error */
4571  confno[0] = '\0';
4572  allowretry = 0;
4573  break;
4574  }
4575  }
4576  if (!ast_strlen_zero(confno)) {
4577  /* Check the validity of the conference */
4578  cnf = find_conf(chan, confno, 1, dynamic, the_pin,
4579  sizeof(the_pin), 1, &confflags);
4580  if (!cnf) {
4581  int too_early = 0;
4582 
4583  cnf = find_conf_realtime(chan, confno, 1, dynamic,
4584  the_pin, sizeof(the_pin), 1, &confflags, &too_early, optargs);
4585  if (rt_schedule && too_early)
4586  allowretry = 0;
4587  }
4588 
4589  if (!cnf) {
4590  if (allowretry) {
4591  confno[0] = '\0';
4592  res = ast_streamfile(chan, "conf-invalid", ast_channel_language(chan));
4593  if (!res)
4594  ast_waitstream(chan, "");
4595  res = -1;
4596  }
4597  } else {
4598  /* Conference requires a pin for specified access level */
4599  int req_pin = !ast_strlen_zero(cnf->pin) ||
4600  (!ast_strlen_zero(cnf->pinadmin) &&
4601  ast_test_flag64(&confflags, CONFFLAG_ADMIN));
4602  /* The following logic was derived from a
4603  * 4 variable truth table and defines which
4604  * circumstances are not exempt from pin
4605  * checking.
4606  * If this needs to be modified, write the
4607  * truth table back out from the boolean
4608  * expression AB+A'D+C', change the erroneous
4609  * result, and rederive the expression.
4610  * Variables:
4611  * A: pin provided?
4612  * B: always prompt?
4613  * C: dynamic?
4614  * D: has users? */
4615  int not_exempt = !cnf->isdynamic;
4616  not_exempt = not_exempt || (!ast_strlen_zero(args.pin) && ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT));
4617  not_exempt = not_exempt || (ast_strlen_zero(args.pin) && cnf->users);
4618  if (req_pin && not_exempt) {
4619  char pin[MAX_PIN] = "";
4620  int j;
4621 
4622  /* Allow the pin to be retried up to 3 times */
4623  for (j = 0; j < 3; j++) {
4624  if (*the_pin && (always_prompt == 0)) {
4625  ast_copy_string(pin, the_pin, sizeof(pin));
4626  res = 0;
4627  } else {
4628  /* Prompt user for pin if pin is required */
4629  ast_test_suite_event_notify("PLAYBACK", "Message: conf-getpin\r\n"
4630  "Channel: %s",
4631  ast_channel_name(chan));
4632  res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
4633  }
4634  if (res >= 0) {
4635  if ((!strcasecmp(pin, cnf->pin) &&
4636  (ast_strlen_zero(cnf->pinadmin) ||
4637  !ast_test_flag64(&confflags, CONFFLAG_ADMIN))) ||
4638  (!ast_strlen_zero(cnf->pinadmin) &&
4639  !strcasecmp(pin, cnf->pinadmin))) {
4640  /* Pin correct */
4641  allowretry = 0;
4642  if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) {
4643  if (!ast_strlen_zero(cnf->adminopts)) {
4644  char *opts = ast_strdupa(cnf->adminopts);
4645  ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
4646  }
4647  } else {
4648  if (!ast_strlen_zero(cnf->useropts)) {
4649  char *opts = ast_strdupa(cnf->useropts);
4650  ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
4651  }
4652  }
4653  /* Run the conference */
4654  ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
4655  res = conf_run(chan, cnf, &confflags, optargs);
4656  break;
4657  } else {
4658  /* Pin invalid */
4659  if (!ast_streamfile(chan, "conf-invalidpin", ast_channel_language(chan))) {
4660  res = ast_waitstream(chan, AST_DIGIT_ANY);
4661  ast_stopstream(chan);
4662  } else {
4663  ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
4664  break;
4665  }
4666  if (res < 0)
4667  break;
4668  pin[0] = res;
4669  pin[1] = '\0';
4670  res = -1;
4671  if (allowretry)
4672  confno[0] = '\0';
4673  }
4674  } else {
4675  /* failed when getting the pin */
4676  res = -1;
4677  allowretry = 0;
4678  /* see if we need to get rid of the conference */
4679  break;
4680  }
4681 
4682  /* Don't retry pin with a static pin */
4683  if (*the_pin && (always_prompt == 0)) {
4684  break;
4685  }
4686  }
4687  } else {
4688  /* No pin required */
4689  allowretry = 0;
4690 
4691  /* For RealTime conferences without a pin
4692  * should still support loading options
4693  */
4694  if (!ast_strlen_zero(cnf->useropts)) {
4695  char *opts = ast_strdupa(cnf->useropts);
4696  ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
4697  }
4698 
4699  /* Run the conference */
4700  res = conf_run(chan, cnf, &confflags, optargs);
4701  }
4702  dispose_conf(cnf);
4703  cnf = NULL;
4704  }
4705  }
4706  } while (allowretry);
4707 
4708  if (cnf)
4709  dispose_conf(cnf);
4710 
4711  return res;
4712 }
4713 
4714 static struct ast_conf_user *find_user(struct ast_conference *conf, const char *callerident)
4715 {
4716  struct ast_conf_user *user = NULL;
4717  int cid;
4718 
4719  if (conf && callerident && sscanf(callerident, "%30d", &cid) == 1) {
4720  user = ao2_find(conf->usercontainer, &cid, 0);
4721  /* reference decremented later in admin_exec */
4722  return user;
4723  }
4724  return NULL;
4725 }
4726 
4727 static int user_listen_volup_cb(void *obj, void *unused, int flags)
4728 {
4729  struct ast_conf_user *user = obj;
4730  tweak_listen_volume(user, VOL_UP);
4731  return 0;
4732 }
4733 
4734 static int user_listen_voldown_cb(void *obj, void *unused, int flags)
4735 {
4736  struct ast_conf_user *user = obj;
4737  tweak_listen_volume(user, VOL_DOWN);
4738  return 0;
4739 }
4740 
4741 static int user_talk_volup_cb(void *obj, void *unused, int flags)
4742 {
4743  struct ast_conf_user *user = obj;
4744  tweak_talk_volume(user, VOL_UP);
4745  return 0;
4746 }
4747 
4748 static int user_talk_voldown_cb(void *obj, void *unused, int flags)
4749 {
4750  struct ast_conf_user *user = obj;
4751  tweak_talk_volume(user, VOL_DOWN);
4752  return 0;
4753 }
4754 
4755 static int user_reset_vol_cb(void *obj, void *unused, int flags)
4756 {
4757  struct ast_conf_user *user = obj;
4758  reset_volumes(user);
4759  return 0;
4760 }
4761 
4762 static int user_chan_cb(void *obj, void *args, int flags)
4763 {
4764  struct ast_conf_user *user = obj;
4765  const char *channel = args;
4766 
4767  if (!strcmp(ast_channel_name(user->chan), channel)) {
4768  return (CMP_MATCH | CMP_STOP);
4769  }
4770 
4771  return 0;
4772 }
4773 
4774 /*! \brief The MeetMeAdmin application
4775 
4776  MeetMeAdmin(confno, command, caller) */
4777 static int admin_exec(struct ast_channel *chan, const char *data) {
4778  char *params;
4779  struct ast_conference *cnf;
4780  struct ast_conf_user *user = NULL;
4781  AST_DECLARE_APP_ARGS(args,
4782  AST_APP_ARG(confno);
4783  AST_APP_ARG(command);
4784  AST_APP_ARG(user);
4785  );
4786  int res = 0;
4787 
4788  if (ast_strlen_zero(data)) {
4789  ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
4790  if (chan) {
4791  pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
4792  }
4793  return -1;
4794  }
4795 
4796  params = ast_strdupa(data);
4797  AST_STANDARD_APP_ARGS(args, params);
4798 
4799  if (!args.command) {
4800  ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
4801  if (chan) {
4802  pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
4803  }
4804  return -1;
4805  }
4806 
4807  AST_LIST_LOCK(&confs);
4808  AST_LIST_TRAVERSE(&confs, cnf, list) {
4809  if (!strcmp(cnf->confno, args.confno))
4810  break;
4811  }
4812 
4813  if (!cnf) {
4814  ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
4816  if (chan) {
4817  pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOTFOUND");
4818  }
4819  return 0;
4820  }
4821 
4823 
4824  if (args.user) {
4825  user = find_user(cnf, args.user);
4826  if (!user) {
4827  ast_log(LOG_NOTICE, "Specified User not found!\n");
4828  res = -2;
4829  goto usernotfound;
4830  }
4831  } else {
4832  /* fail for commands that require a user */
4833  switch (*args.command) {
4834  case 'm': /* Unmute */
4835  case 'M': /* Mute */
4836  case 't': /* Lower user's talk volume */
4837  case 'T': /* Raise user's talk volume */
4838  case 'u': /* Lower user's listen volume */
4839  case 'U': /* Raise user's listen volume */
4840  case 'r': /* Reset user's volume level */
4841  case 'k': /* Kick user */
4842  res = -2;
4843  ast_log(LOG_NOTICE, "No user specified!\n");
4844  goto usernotfound;
4845  default:
4846  break;
4847  }
4848  }
4849 
4850  switch (*args.command) {
4851  case 76: /* L: Lock */
4852  cnf->locked = 1;
4853  break;
4854  case 108: /* l: Unlock */
4855  cnf->locked = 0;
4856  break;
4857  case 75: /* K: kick all users */
4858  ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_kickme_cb, NULL);
4859  break;
4860  case 101: /* e: Eject last user*/
4861  {
4862  int max_no = 0;
4863  RAII_VAR(struct ast_conf_user *, eject_user, NULL, ao2_cleanup);
4864 
4865  ao2_callback(cnf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
4866  eject_user = ao2_find(cnf->usercontainer, &max_no, 0);
4867  if (!eject_user) {
4868  res = -1;
4869  ast_log(LOG_NOTICE, "No last user to kick!\n");
4870  break;
4871  }
4872 
4873  if (!ast_test_flag64(&eject_user->userflags, CONFFLAG_ADMIN)) {
4874  eject_user->adminflags |= ADMINFLAG_KICKME;
4875  } else {
4876  res = -1;
4877  ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
4878  }
4879  break;
4880  }
4881  case 77: /* M: Mute */
4882  user->adminflags |= ADMINFLAG_MUTED;
4883  break;
4884  case 78: /* N: Mute all (non-admin) users */
4885  ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_muted_cb, &cnf);
4886  break;
4887  case 109: /* m: Unmute */
4889  break;
4890  case 110: /* n: Unmute all users */
4891  ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, NULL);
4892  break;
4893  case 107: /* k: Kick user */
4894  user->adminflags |= ADMINFLAG_KICKME;
4895  break;
4896  case 118: /* v: Lower all users listen volume */
4897  ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_voldown_cb, NULL);
4898  break;
4899  case 86: /* V: Raise all users listen volume */
4900  ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_volup_cb, NULL);
4901  break;
4902  case 115: /* s: Lower all users speaking volume */
4903  ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_voldown_cb, NULL);
4904  break;
4905  case 83: /* S: Raise all users speaking volume */
4906  ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_volup_cb, NULL);
4907  break;
4908  case 82: /* R: Reset all volume levels */
4909  ao2_callback(cnf->usercontainer, OBJ_NODATA, user_reset_vol_cb, NULL);
4910  break;
4911  case 114: /* r: Reset user's volume level */
4912  reset_volumes(user);
4913  break;
4914  case 85: /* U: Raise user's listen volume */
4915  tweak_listen_volume(user, VOL_UP);
4916  break;
4917  case 117: /* u: Lower user's listen volume */
4918  tweak_listen_volume(user, VOL_DOWN);
4919  break;
4920  case 84: /* T: Raise user's talk volume */
4921  tweak_talk_volume(user, VOL_UP);
4922  break;
4923  case 116: /* t: Lower user's talk volume */
4924  tweak_talk_volume(user, VOL_DOWN);
4925  break;
4926  case 'E': /* E: Extend conference */
4927  if (rt_extend_conf(args.confno)) {
4928  res = -1;
4929  }
4930  break;
4931  }
4932 
4933  if (args.user) {
4934  /* decrement reference from find_user */
4935  ao2_ref(user, -1);
4936  }
4937 usernotfound:
4939 
4940  dispose_conf(cnf);
4941  if (chan) {
4942  pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", res == -2 ? "NOTFOUND" : res ? "FAILED" : "OK");
4943  }
4944 
4945  return 0;
4946 }
4947 
4948 /*! \brief The MeetMeChannelAdmin application
4949  MeetMeChannelAdmin(channel, command) */
4950 static int channel_admin_exec(struct ast_channel *chan, const char *data) {
4951  char *params;
4952  struct ast_conference *conf = NULL;
4953  struct ast_conf_user *user = NULL;
4954  AST_DECLARE_APP_ARGS(args,
4955  AST_APP_ARG(channel);
4956  AST_APP_ARG(command);
4957  );
4958 
4959  if (ast_strlen_zero(data)) {
4960  ast_log(LOG_WARNING, "MeetMeChannelAdmin requires two arguments!\n");
4961  return -1;
4962  }
4963 
4964  params = ast_strdupa(data);
4965  AST_STANDARD_APP_ARGS(args, params);
4966 
4967  if (!args.channel) {
4968  ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a channel name!\n");
4969  return -1;
4970  }
4971 
4972  if (!args.command) {
4973  ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a command!\n");
4974  return -1;
4975  }
4976 
4977  AST_LIST_LOCK(&confs);
4978  AST_LIST_TRAVERSE(&confs, conf, list) {
4979  if ((user = ao2_callback(conf->usercontainer, 0, user_chan_cb, args.channel))) {
4980  break;
4981  }
4982  }
4983 
4984  if (!user) {
4985  ast_log(LOG_NOTICE, "Specified user (%s) not found\n", args.channel);
4987  return 0;
4988  }
4989 
4990  /* perform the specified action */
4991  switch (*args.command) {
4992  case 77: /* M: Mute */
4993  user->adminflags |= ADMINFLAG_MUTED;
4994  break;
4995  case 109: /* m: Unmute */
4996  user->adminflags &= ~ADMINFLAG_MUTED;
4997  break;
4998  case 107: /* k: Kick user */
4999  user->adminflags |= ADMINFLAG_KICKME;
5000  break;
5001  default: /* unknown command */
5002  ast_log(LOG_WARNING, "Unknown MeetMeChannelAdmin command '%s'\n", args.command);
5003  break;
5004  }
5005  ao2_ref(user, -1);
5007 
5008  return 0;
5009 }
5010 
5011 static int meetmemute(struct mansession *s, const struct message *m, int mute)
5012 {
5013  struct ast_conference *conf;
5014  struct ast_conf_user *user;
5015  const char *confid = astman_get_header(m, "Meetme");
5016  char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
5017  int userno;
5018 
5019  if (ast_strlen_zero(confid)) {
5020  astman_send_error(s, m, "Meetme conference not specified");
5021  return 0;
5022  }
5023 
5024  if (ast_strlen_zero(userid)) {
5025  astman_send_error(s, m, "Meetme user number not specified");
5026  return 0;
5027  }
5028 
5029  userno = strtoul(userid, &userid, 10);
5030 
5031  if (*userid) {
5032  astman_send_error(s, m, "Invalid user number");
5033  return 0;
5034  }
5035 
5036  /* Look in the conference list */
5037  AST_LIST_LOCK(&confs);
5038  AST_LIST_TRAVERSE(&confs, conf, list) {
5039  if (!strcmp(confid, conf->confno))
5040  break;
5041  }
5042 
5043  if (!conf) {
5045  astman_send_error(s, m, "Meetme conference does not exist");
5046  return 0;
5047  }
5048 
5049  user = ao2_find(conf->usercontainer, &userno, 0);
5050 
5051  if (!user) {
5053  astman_send_error(s, m, "User number not found");
5054  return 0;
5055  }
5056 
5057  if (mute)
5058  user->adminflags |= ADMINFLAG_MUTED; /* request user muting */
5059  else
5060  user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST); /* request user unmuting */
5061 
5063 
5064  ast_log(LOG_NOTICE, "Requested to %smute conf %s user %d userchan %s uniqueid %s\n", mute ? "" : "un", conf->confno, user->user_no, ast_channel_name(user->chan), ast_channel_uniqueid(user->chan));
5065 
5066  ao2_ref(user, -1);
5067  astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
5068  return 0;
5069 }
5070 
5071 static int action_meetmemute(struct mansession *s, const struct message *m)
5072 {
5073  return meetmemute(s, m, 1);
5074 }
5075 
5076 static int action_meetmeunmute(struct mansession *s, const struct message *m)
5077 {
5078  return meetmemute(s, m, 0);
5079 }
5080 
5081 static int action_meetmelist(struct mansession *s, const struct message *m)
5082 {
5083  const char *actionid = astman_get_header(m, "ActionID");
5084  const char *conference = astman_get_header(m, "Conference");
5085  char idText[80] = "";
5086  struct ast_conference *cnf;
5087  struct ast_conf_user *user;
5088  struct ao2_iterator user_iter;
5089  int total = 0;
5090 
5091  if (!ast_strlen_zero(actionid))
5092  snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
5093 
5094  if (AST_LIST_EMPTY(&confs)) {
5095  astman_send_error(s, m, "No active conferences.");
5096  return 0;
5097  }
5098 
5099  astman_send_listack(s, m, "Meetme user list will follow", "start");
5100 
5101  /* Find the right conference */
5102  AST_LIST_LOCK(&confs);
5103  AST_LIST_TRAVERSE(&confs, cnf, list) {
5104  /* If we ask for one particular, and this isn't it, skip it */
5105  if (!ast_strlen_zero(conference) && strcmp(cnf->confno, conference))
5106  continue;
5107 
5108  /* Show all the users */
5109  user_iter = ao2_iterator_init(cnf->usercontainer, 0);
5110  while ((user = ao2_iterator_next(&user_iter))) {
5111  total++;
5112  astman_append(s,
5113  "Event: MeetmeList\r\n"
5114  "%s"
5115  "Conference: %s\r\n"
5116  "UserNumber: %d\r\n"
5117  "CallerIDNum: %s\r\n"
5118  "CallerIDName: %s\r\n"
5119  "ConnectedLineNum: %s\r\n"
5120  "ConnectedLineName: %s\r\n"
5121  "Channel: %s\r\n"
5122  "Admin: %s\r\n"
5123  "Role: %s\r\n"
5124  "MarkedUser: %s\r\n"
5125  "Muted: %s\r\n"
5126  "Talking: %s\r\n"
5127  "\r\n",
5128  idText,
5129  cnf->confno,
5130  user->user_no,
5131  S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
5132  S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<no name>"),
5133  S_COR(ast_channel_connected(user->chan)->id.number.valid, ast_channel_connected(user->chan)->id.number.str, "<unknown>"),
5134  S_COR(ast_channel_connected(user->chan)->id.name.valid, ast_channel_connected(user->chan)->id.name.str, "<no name>"),
5135  ast_channel_name(user->chan),
5136  ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "Yes" : "No",
5137  ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "Listen only" : ast_test_flag64(&user->userflags, CONFFLAG_TALKER) ? "Talk only" : "Talk and listen",
5138  ast_test_flag64(&user->userflags, CONFFLAG_MARKEDUSER) ? "Yes" : "No",
5139  user->adminflags & ADMINFLAG_MUTED ? "By admin" : user->adminflags & ADMINFLAG_SELFMUTED ? "By self" : "No",
5140  user->talking > 0 ? "Yes" : user->talking == 0 ? "No" : "Not monitored");
5141  ao2_ref(user, -1);
5142  }
5143  ao2_iterator_destroy(&user_iter);
5144  }
5146 
5147  /* Send final confirmation */
5148  astman_send_list_complete_start(s, m, "MeetmeListComplete", total);
5150  return 0;
5151 }
5152 
5153 static int action_meetmelistrooms(struct mansession *s, const struct message *m)
5154 {
5155  const char *actionid = astman_get_header(m, "ActionID");
5156  char idText[80] = "";
5157  struct ast_conference *cnf;
5158  int totalitems = 0;
5159  int hr, min, sec;
5160  time_t now;
5161  char markedusers[5];
5162 
5163  if (!ast_strlen_zero(actionid)) {
5164  snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
5165  }
5166 
5167  if (AST_LIST_EMPTY(&confs)) {
5168  astman_send_error(s, m, "No active conferences.");
5169  return 0;
5170  }
5171 
5172  astman_send_listack(s, m, "Meetme conferences will follow", "start");
5173 
5174  now = time(NULL);
5175 
5176  /* Traverse the conference list */
5177  AST_LIST_LOCK(&confs);
5178  AST_LIST_TRAVERSE(&confs, cnf, list) {
5179  totalitems++;
5180 
5181  if (cnf->markedusers == 0) {
5182  strcpy(markedusers, "N/A");
5183  } else {
5184  sprintf(markedusers, "%.4d", cnf->markedusers);
5185  }
5186  hr = (now - cnf->start) / 3600;
5187  min = ((now - cnf->start) % 3600) / 60;
5188  sec = (now - cnf->start) % 60;
5189 
5190  astman_append(s,
5191  "Event: MeetmeListRooms\r\n"
5192  "%s"
5193  "Conference: %s\r\n"
5194  "Parties: %d\r\n"
5195  "Marked: %s\r\n"
5196  "Activity: %2.2d:%2.2d:%2.2d\r\n"
5197  "Creation: %s\r\n"
5198  "Locked: %s\r\n"
5199  "\r\n",
5200  idText,
5201  cnf->confno,
5202  cnf->users,
5203  markedusers,
5204  hr, min, sec,
5205  cnf->isdynamic ? "Dynamic" : "Static",
5206  cnf->locked ? "Yes" : "No");
5207  }
5209 
5210  /* Send final confirmation */
5211  astman_send_list_complete_start(s, m, "MeetmeListRoomsComplete", totalitems);
5213  return 0;
5214 }
5215 
5216 /*! \internal
5217  * \brief creates directory structure and assigns absolute path from relative paths for filenames
5218  *
5219  * \param filename contains the absolute or relative path to the desired file
5220  * \param buffer stores completed filename, absolutely must be a buffer of PATH_MAX length
5221  */
5222 static void filename_parse(char *filename, char *buffer)
5223 {
5224  char *slash;
5225  if (ast_strlen_zero(filename)) {
5226  ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
5227  } else if (filename[0] != '/') {
5228  snprintf(buffer, PATH_MAX, "%s/meetme/%s", ast_config_AST_SPOOL_DIR, filename);
5229  } else {
5230  ast_copy_string(buffer, filename, PATH_MAX);
5231  }
5232 
5233  slash = buffer;
5234  if ((slash = strrchr(slash, '/'))) {
5235  *slash = '\0';
5236  ast_mkdir(buffer, 0777);
5237  *slash = '/';
5238  }
5239 }
5240 
5241 static void *recordthread(void *args)
5242 {
5243  struct ast_conference *cnf = args;
5244  struct ast_frame *f = NULL;
5245  int flags;
5246  struct ast_filestream *s = NULL;
5247  int res = 0;
5248  int x;
5249  const char *oldrecordingfilename = NULL;
5250  char filename_buffer[PATH_MAX];
5251 
5252  if (!cnf || !cnf->lchan) {
5253  pthread_exit(0);
5254  }
5255 
5256  filename_buffer[0] = '\0';
5257  filename_parse(cnf->recordingfilename, filename_buffer);
5258 
5259  ast_stopstream(cnf->lchan);
5260  flags = O_CREAT | O_TRUNC | O_WRONLY;
5261 
5262 
5263  cnf->recording = MEETME_RECORD_ACTIVE;
5264  while (ast_waitfor(cnf->lchan, -1) > -1) {
5265  if (cnf->recording == MEETME_RECORD_TERMINATE) {
5266  AST_LIST_LOCK(&confs);
5268  break;
5269  }
5270  if (!s && !(ast_strlen_zero(filename_buffer)) && (filename_buffer != oldrecordingfilename)) {
5271  s = ast_writefile(filename_buffer, cnf->recordingformat, NULL, flags, 0, AST_FILE_MODE);
5272  oldrecordingfilename = filename_buffer;
5273  }
5274 
5275  f = ast_read(cnf->lchan);
5276  if (!f) {
5277  res = -1;
5278  break;
5279  }
5280  if (f->frametype == AST_FRAME_VOICE) {
5281  ast_mutex_lock(&cnf->listenlock);
5282  for (x = 0; x < AST_FRAME_BITS; x++) {
5283  /* Free any translations that have occured */
5284  if (cnf->transframe[x]) {
5285  ast_frfree(cnf->transframe[x]);
5286  cnf->transframe[x] = NULL;
5287  }
5288  }
5289  if (cnf->origframe)
5290  ast_frfree(cnf->origframe);
5291  cnf->origframe = ast_frdup(f);
5292  ast_mutex_unlock(&cnf->listenlock);
5293  if (s)
5294  res = ast_writestream(s, f);
5295  if (res) {
5296  ast_frfree(f);
5297  break;
5298  }
5299  }
5300  ast_frfree(f);
5301  }
5302  cnf->recording = MEETME_RECORD_OFF;
5303  if (s)
5304  ast_closestream(s);
5305 
5306  pthread_exit(0);
5307 }
5308 
5309 /*! \brief Callback for devicestate providers */
5310 static enum ast_device_state meetmestate(const char *data)
5311 {
5312  struct ast_conference *conf;
5313 
5314  /* Find conference */
5315  AST_LIST_LOCK(&confs);
5316  AST_LIST_TRAVERSE(&confs, conf, list) {
5317  if (!strcmp(data, conf->confno))
5318  break;
5319  }
5321  if (!conf)
5322  return AST_DEVICE_INVALID;
5323 
5324 
5325  /* SKREP to fill */
5326  if (!conf->users)
5327  return AST_DEVICE_NOT_INUSE;
5328 
5329  return AST_DEVICE_INUSE;
5330 }
5331 
5332 static void meetme_set_defaults(void)
5333 {
5334  /* Scheduling support is off by default */
5335  rt_schedule = 0;
5336  fuzzystart = 0;
5337  earlyalert = 0;
5338  endalert = 0;
5339  extendby = 0;
5340 
5341  /* Logging of participants defaults to ON for compatibility reasons */
5342  rt_log_members = 1;
5343 
5344  /* Set default number of buffers to be allocated. */
5346 }
5347 
5348 static void load_config_meetme(int reload)
5349 {
5350  struct ast_config *cfg;
5351  struct ast_flags config_flags = { 0 };
5352  const char *val;
5353 
5354  if (!reload) {
5355  meetme_set_defaults();
5356  }
5357 
5358  if (!(cfg = ast_config_load(CONFIG_FILE_NAME, config_flags))) {
5359  return;
5360  } else if (cfg == CONFIG_STATUS_FILEINVALID) {
5361  ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format. Aborting.\n");
5362  return;
5363  }
5364 
5365  if (reload) {
5366  meetme_set_defaults();
5367  }
5368 
5369  if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
5370  if ((sscanf(val, "%30d", &audio_buffers) != 1)) {
5371  ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
5373  } else if ((audio_buffers < DAHDI_DEFAULT_NUM_BUFS) || (audio_buffers > DAHDI_MAX_NUM_BUFS)) {
5374  ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
5375  DAHDI_DEFAULT_NUM_BUFS, DAHDI_MAX_NUM_BUFS);
5377  }
5379  ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
5380  }
5381 
5382  if ((val = ast_variable_retrieve(cfg, "general", "schedule")))
5383  rt_schedule = ast_true(val);
5384  if ((val = ast_variable_retrieve(cfg, "general", "logmembercount")))
5385  rt_log_members = ast_true(val);
5386  if ((val = ast_variable_retrieve(cfg, "general", "fuzzystart"))) {
5387  if ((sscanf(val, "%30d", &fuzzystart) != 1)) {
5388  ast_log(LOG_WARNING, "fuzzystart must be a number, not '%s'\n", val);
5389  fuzzystart = 0;
5390  }
5391  }
5392  if ((val = ast_variable_retrieve(cfg, "general", "earlyalert"))) {
5393  if ((sscanf(val, "%30d", &earlyalert) != 1)) {
5394  ast_log(LOG_WARNING, "earlyalert must be a number, not '%s'\n", val);
5395  earlyalert = 0;
5396  }
5397  }
5398  if ((val = ast_variable_retrieve(cfg, "general", "endalert"))) {
5399  if ((sscanf(val, "%30d", &endalert) != 1)) {
5400  ast_log(LOG_WARNING, "endalert must be a number, not '%s'\n", val);
5401  endalert = 0;
5402  }
5403  }
5404  if ((val = ast_variable_retrieve(cfg, "general", "extendby"))) {
5405  if ((sscanf(val, "%30d", &extendby) != 1)) {
5406  ast_log(LOG_WARNING, "extendby must be a number, not '%s'\n", val);
5407  extendby = 0;
5408  }
5409  }
5410 
5411  ast_config_destroy(cfg);
5412 }
5413 
5414 static int acf_meetme_info_eval(const char *keyword, const struct ast_conference *conf)
5415 {
5416  if (!strcasecmp("lock", keyword)) {
5417  return conf->locked;
5418  } else if (!strcasecmp("parties", keyword)) {
5419  return conf->users;
5420  } else if (!strcasecmp("activity", keyword)) {
5421  time_t now;
5422  now = time(NULL);
5423  return (now - conf->start);
5424  } else if (!strcasecmp("dynamic", keyword)) {
5425  return conf->isdynamic;
5426  } else {
5427  return -1;
5428  }
5429 
5430 }
5431 
5432 static int acf_meetme_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
5433 {
5434  struct ast_conference *conf;
5435  char *parse;
5436  int result = -2; /* only non-negative numbers valid, -1 is used elsewhere */
5437  AST_DECLARE_APP_ARGS(args,
5438  AST_APP_ARG(keyword);
5439  AST_APP_ARG(confno);
5440  );
5441 
5442  if (ast_strlen_zero(data)) {
5443  ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires two arguments\n");
5444  return -1;
5445  }
5446 
5447  parse = ast_strdupa(data);
5448  AST_STANDARD_APP_ARGS(args, parse);
5449 
5450  if (ast_strlen_zero(args.keyword)) {
5451  ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a keyword\n");
5452  return -1;
5453  }
5454 
5455  if (ast_strlen_zero(args.confno)) {
5456  ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a conference number\n");
5457  return -1;
5458  }
5459 
5460  AST_LIST_LOCK(&confs);
5461  AST_LIST_TRAVERSE(&confs, conf, list) {
5462  if (!strcmp(args.confno, conf->confno)) {
5463  result = acf_meetme_info_eval(args.keyword, conf);
5464  break;
5465  }
5466  }
5468 
5469  if (result > -1) {
5470  snprintf(buf, len, "%d", result);
5471  } else if (result == -1) {
5472  ast_log(LOG_NOTICE, "Error: invalid keyword: '%s'\n", args.keyword);
5473  snprintf(buf, len, "0");
5474  } else if (result == -2) {
5475  ast_log(LOG_NOTICE, "Error: conference (%s) not found\n", args.confno);
5476  snprintf(buf, len, "0");
5477  }
5478 
5479  return 0;
5480 }
5481 
5482 static struct ast_custom_function meetme_info_acf = {
5483  .name = "MEETME_INFO",
5484  .read = acf_meetme_info,
5485 };
5486 
5487 static int load_config(int reload)
5488 {
5489  load_config_meetme(reload);
5490  return 0;
5491 }
5492 
5493 static int unload_module(void)
5494 {
5495  int res = 0;
5496 
5497  ast_cli_unregister_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
5498  res = ast_manager_unregister("MeetmeMute");
5499  res |= ast_manager_unregister("MeetmeUnmute");
5500  res |= ast_manager_unregister("MeetmeList");
5501  res |= ast_manager_unregister("MeetmeListRooms");
5502  res |= ast_unregister_application(app4);
5503  res |= ast_unregister_application(app3);
5504  res |= ast_unregister_application(app2);
5505  res |= ast_unregister_application(app);
5506 
5507  ast_devstate_prov_del("Meetme");
5508 
5509  res |= ast_custom_function_unregister(&meetme_info_acf);
5510  ast_unload_realtime("meetme");
5511 
5512  meetme_stasis_cleanup();
5513 
5514  return res;
5515 }
5516 
5517 /*!
5518  * \brief Load the module
5519  *
5520  * Module loading including tests for configuration or dependencies.
5521  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
5522  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
5523  * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
5524  * configuration file or other non-critical problem return
5525  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
5526  */
5527 static int load_module(void)
5528 {
5529  int res = 0;
5530 
5531  res |= load_config(0);
5532 
5533  res |= meetme_stasis_init();
5534 
5535  ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
5536  res |= ast_manager_register_xml("MeetmeMute", EVENT_FLAG_CALL, action_meetmemute);
5537  res |= ast_manager_register_xml("MeetmeUnmute", EVENT_FLAG_CALL, action_meetmeunmute);
5538  res |= ast_manager_register_xml("MeetmeList", EVENT_FLAG_REPORTING, action_meetmelist);
5539  res |= ast_manager_register_xml("MeetmeListRooms", EVENT_FLAG_REPORTING, action_meetmelistrooms);
5544 
5545  res |= ast_devstate_prov_add("Meetme", meetmestate);
5546 
5547  res |= ast_custom_function_register(&meetme_info_acf);
5548  ast_realtime_require_field("meetme", "confno", RQ_UINTEGER2, 3, "members", RQ_UINTEGER1, 3, NULL);
5549 
5550  return res;
5551 }
5552 
5553 static int reload(void)
5554 {
5555  ast_unload_realtime("meetme");
5556  return load_config(1);
5557 }
5558 
5559 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "MeetMe conference bridge",
5560  .support_level = AST_MODULE_SUPPORT_DEPRECATED,
5561  .load = load_module,
5562  .unload = unload_module,
5563  .reload = reload,
5564  .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
5565  .optional_modules = "func_speex",
5566 );
const char * name
Definition: pbx.h:119
struct ast_variable * next
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
Definition: channel.c:1574
static int conf_free(struct ast_conference *conf)
Remove the conference from the list and free it.
Definition: app_meetme.c:1976
const char * warning_sound
Definition: app_meetme.c:919
Main Channel structure associated with a channel.
enum ast_getdata_result ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout)
Plays a stream and gets DTMF data from a channel.
Definition: main/app.c:188
Music on hold handling.
ast_device_state
Device States.
Definition: devicestate.h:52
#define ast_frdup(fr)
Copies a frame.
char * str
Subscriber phone number (Malloced)
Definition: channel.h:291
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1293
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
Asterisk locking-related definitions:
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:3310
Asterisk main include file. File version handling, generic pbx functions.
static int dispose_conf(struct ast_conference *conf)
Decrement reference counts, as incremented by find_conf()
Definition: app_meetme.c:2065
struct timeval start_time
Definition: app_meetme.c:915
struct ast_channel * chan
Definition: app_meetme.c:854
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612
uint64_t ast_format_compatibility_format2bitfield(const struct ast_format *format)
Convert a format structure to its respective bitfield.
#define AST_OPTION_TXGAIN
int pbx_exec(struct ast_channel *c, struct ast_app *app, const char *data)
Execute an application.
Definition: pbx_app.c:471
int stasis_message_router_add(struct stasis_message_router *router, struct stasis_message_type *message_type, stasis_subscription_cb callback, void *data)
Add a route to a message router.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
ast_mutex_t playlock
Definition: app_meetme.c:851
struct ast_party_id id
Connected party ID.
Definition: channel.h:458
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
Device state management.
Support for translation of data formats. translate.c.
int ast_indicate(struct ast_channel *chan, int condition)
Indicates condition of channel.
Definition: channel.c:4277
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
struct ast_party_name name
Subscriber name.
Definition: channel.h:340
pthread_t recordthread
Definition: app_meetme.c:868
struct ast_json * blob
#define STASIS_MESSAGE_TYPE_INIT(name)
Boiler-plate messaging macro for initializing message types.
Definition: stasis.h:1493
struct ast_channel_snapshot * snapshot
Convenient Signal Processing routines.
int ast_softhangup(struct ast_channel *chan, int cause)
Softly hangup up a channel.
Definition: channel.c:2471
static int conf_exec(struct ast_channel *chan, const char *data)
The meetme() application.
Definition: app_meetme.c:4389
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
int ast_json_object_update(struct ast_json *object, struct ast_json *other)
Update object with all of the fields of other.
Definition: json.c:426
void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count)
Start the list complete event.
Definition: manager.c:3467
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
descriptor for a cli entry.
Definition: cli.h:171
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
unsigned int isdynamic
Definition: app_meetme.c:865
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
Definition: astobj2.h:1693
char * recordingformat
Definition: app_meetme.c:872
int ast_unload_realtime(const char *family)
Release any resources cached for a realtime family.
Definition: main/config.c:3576
struct ast_dsp * ast_dsp_new(void)
Allocates a new dsp, assumes 8khz for internal sample rate.
Definition: dsp.c:1758
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1739
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a list container.
Definition: astobj2.h:1327
Structure for variables, used for configurations and for channel variables.
#define CONFFLAG_DONT_DENOISE
Definition: app_meetme.c:763
static int count_exec(struct ast_channel *chan, const char *data)
The MeetmeCount application.
Definition: app_meetme.c:4344
static int load_module(void)
Load the module.
Definition: app_meetme.c:5527
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Definition: time.h:117
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:439
Test Framework API.
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4257
#define STASIS_MESSAGE_TYPE_CLEANUP(name)
Boiler-plate messaging macro for cleaning up message types.
Definition: stasis.h:1515
int ast_devstate_prov_del(const char *label)
Remove device state provider.
Definition: devicestate.c:418
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
ast_mutex_t listenlock
Definition: app_meetme.c:852
ast_channel_state
ast_channel states
Definition: channelstate.h:35
char * str
Subscriber name (Malloced)
Definition: channel.h:264
int ast_say_digits(struct ast_channel *chan, int num, const char *ints, const char *lang)
says digits
Definition: channel.c:8253
Definition: astman.c:222
Dialing API.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
static struct ast_conference * build_conf(const char *confno, const char *pin, const char *pinadmin, int make, int dynamic, int refcount, const struct ast_channel *chan, struct ast_test *test)
Find or create a conference.
Definition: app_meetme.c:1428
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
Add device state provider.
Definition: devicestate.c:391
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
long warning_freq
Definition: app_meetme.c:918
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:450
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:3421
struct ast_frame * ast_translate(struct ast_trans_pvt *tr, struct ast_frame *f, int consume)
translates one or more frames Apply an input frame into the translator and receive zero or one output...
Definition: translate.c:566
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:107
struct ast_channel * ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds, int *exception, int *outfd, int *ms)
Waits for activity on a group of channels.
Definition: channel.c:2988
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
const char * useropts
Definition: app_meetme.c:877
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition: file.c:1141
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
Definition: linkedlists.h:856
int ast_channel_setoption(struct ast_channel *channel, int option, void *data, int datalen, int block)
Sets an option on a channel.
Definition: channel.c:7422
Structure used to handle a large number of boolean flags == used only in app_dial?
Definition: utils.h:204
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:7776
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Update realtime configuration.
Definition: main/config.c:3659
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition: lock.h:757
unsigned int locked
Definition: app_meetme.c:866
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
struct ast_frame_subclass subclass
#define MAX_LANGUAGE
Definition: channel.h:172
int ast_json_object_set(struct ast_json *object, const char *key, struct ast_json *value)
Set a field in a JSON object.
Definition: json.c:414
Utility functions.
Blob of data associated with a channel.
unsigned int gmuted
Definition: app_meetme.c:867
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition: manager.c:3050
#define CONFFLAG_INTROMSG
Definition: app_meetme.c:758
struct ast_channel * ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
Requests a channel.
Definition: channel.c:6354
All configuration options for http media cache.
int ast_record_review(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, const char *path)
Allow to record message and have a review option.
Definition: main/app.c:2646
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
struct ast_party_id id
Caller party ID.
Definition: channel.h:420
Configuration File Parser.
#define CONFFLAG_KILL_LAST_MAN_STANDING
Definition: app_meetme.c:761
struct stasis_topic * ast_channel_topic_all(void)
A topic which publishes the events for all channels.
u-Law to Signed linear conversion
#define ast_config_load(filename, flags)
Load a config file.
enum ast_format_cmp_res ast_format_cmp(const struct ast_format *format1, const struct ast_format *format2)
Compare two formats.
Definition: format.c:201
struct ast_trans_pvt * ast_translator_build_path(struct ast_format *dest, struct ast_format *source)
Builds a translator path Build a path (possibly NULL) from source to dest.
Definition: translate.c:486
int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt,...)
Tells Asterisk the State for Device is changed.
Definition: devicestate.c:510
int actual
Definition: app_meetme.c:900
const char * end_sound
Definition: app_meetme.c:920
General Asterisk PBX channel definitions.
Asterisk JSON abstraction layer.
int ast_realtime_require_field(const char *family,...) attribute_sentinel
Inform realtime what fields that may be stored.
Definition: main/config.c:3549
const char * bookid
Definition: app_meetme.c:879
Asterisk file paths, configured in asterisk.conf.
#define AST_FRIENDLY_OFFSET
Offset into a frame's data buffer.
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition: manager.c:3475
int ast_set_read_format(struct ast_channel *chan, struct ast_format *format)
Sets read format on channel chan.
Definition: channel.c:5762
Definition: dsp.c:407
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define AST_MAX_EXTENSION
Definition: channel.h:134
The MeetMe User object.
Definition: app_meetme.c:904
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:87
In case you didn't read that giant block of text above the mansession_session struct, the mansession is named this solely to keep the API the same in Asterisk. This structure really represents data that is different from Manager action to Manager action. The mansession_session pointer contained within points to session-specific data.
Definition: manager.c:1785
struct timeval ast_samp2tv(unsigned int _nsamp, unsigned int _rate)
Returns a timeval corresponding to the duration of n samples at rate r. Useful to convert samples to ...
Definition: time.h:282
int ast_atomic_dec_and_test(volatile int *p)
decrement *p by 1 and return true if the variable has reached 0.
Definition: lock.h:767
#define ast_format_cap_append(cap, format, framing)
Add format capability to capabilities structure.
Definition: format_cap.h:99
static enum ast_device_state meetmestate(const char *data)
Callback for devicestate providers.
Definition: app_meetme.c:5310
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:283
#define ast_debug(level,...)
Log a DEBUG message.
int ast_set_write_format(struct ast_channel *chan, struct ast_format *format)
Sets write format on channel chan.
Definition: channel.c:5803
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
Definition: pbx.c:4175
#define ast_format_cap_alloc(flags)
Allocate a new ast_format_cap structure.
Definition: format_cap.h:49
struct ast_str * ast_manager_build_channel_state_string(const struct ast_channel_snapshot *snapshot)
Generate the AMI message body from a channel snapshot.
Structure to describe a channel "technology", ie a channel driver See for examples: ...
Definition: channel.h:628
Core PBX routines and definitions.
char * ast_strptime(const char *s, const char *format, struct ast_tm *tm)
Special version of strptime(3) which places the answer in the common structure ast_tm. Also, unlike strptime(3), ast_strptime() initializes its memory prior to use.
Definition: localtime.c:2550
int ast_app_parse_options64(const struct ast_app_option *options, struct ast_flags64 *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3071
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:291
#define ast_test_suite_event_notify(s, f,...)
Definition: test.h:189
struct stasis_topic * ast_channel_topic(struct ast_channel *chan)
A topic which publishes the events for a particular channel.
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:8057
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
Definition: linkedlists.h:225
char pin[MAX_PIN]
Definition: app_meetme.c:873
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: main/config.c:3622
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
struct ast_flags64 userflags
Definition: app_meetme.c:906
#define DEFAULT_AUDIO_BUFFERS
Definition: app_meetme.c:659
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: utils.c:2199
Support for dynamic strings.
Definition: strings.h:623
Format capabilities structure, holds formats + preference order + etc.
Definition: format_cap.c:54
Default structure for translators, with the basic fields and buffers, all allocated as part of the sa...
Definition: translate.h:213
#define AST_OPTION_RXGAIN
Media Format Bitfield Compatibility API.
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
#define ao2_unlink(container, obj)
Remove an object from a container.
Definition: astobj2.h:1578
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
#define DATE_FORMAT
Definition: app_meetme.c:662
enum recording_state recording
Definition: app_meetme.c:864
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Definition: extconf.c:2282
int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime_sec, const char *fmt, int *duration, int *sound_duration, int silencethreshold, int maxsilence_ms, const char *path)
Record a file based on input from a channel. Use default accept and cancel DTMF. This function will p...
Definition: main/app.c:2154
struct ast_filestream * ast_writefile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
Starts writing a file.
Definition: file.c:1423
time_t kicktime
Definition: app_meetme.c:914
int ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
Turn on music on hold on a given channel.
Definition: channel.c:7766
char namerecloc[PATH_MAX]
Definition: app_meetme.c:841
int desired
Definition: app_meetme.c:899
static int rt_log_members
Definition: app_meetme.c:825
int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority)
Definition: pbx.c:8781
struct stasis_message * ast_channel_blob_create(struct ast_channel *chan, struct stasis_message_type *type, struct ast_json *blob)
Creates a ast_channel_blob message.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition: stasis.c:1511
#define AST_MAX_CONTEXT
Definition: channel.h:135
union ast_frame::@224 data
char namerecloc[PATH_MAX]
Definition: app_meetme.c:912
char * command
Definition: cli.h:186
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:1111
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2541
#define CONFFLAG_NO_AUDIO_UNTIL_UP
Definition: app_meetme.c:757
unsigned int flags
int ast_write(struct ast_channel *chan, struct ast_frame *frame)
Write a frame to a channel This function writes the given frame to the indicated channel.
Definition: channel.c:5144
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition: localtime.c:2524
const char * adminopts
Definition: app_meetme.c:878
time_t jointime
Definition: app_meetme.c:913
structure to hold users read from users.conf
Structure used to handle boolean flags.
Definition: utils.h:199
pthread_attr_t attr
Definition: app_meetme.c:870
const char * usage
Definition: cli.h:177
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
struct ast_frame ast_null_frame
Definition: main/frame.c:79
int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence)
Process the audio frame for silence.
Definition: dsp.c:1488
static const char gain_map[]
Map 'volume' levels from -5 through +5 into decibel (dB) settings for channel drivers.
Definition: app_meetme.c:937
STASIS_MESSAGE_TYPE_DEFN_LOCAL(cdr_sync_message_type)
A message type used to synchronize with the CDR topic.
void stasis_message_router_unsubscribe(struct stasis_message_router *router)
Unsubscribe the router from the upstream topic.
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:681
This structure is allocated by file.c in one chunk, together with buf_size and desc_size bytes of mem...
Definition: mod_format.h:101
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
Definition: channel.c:3162
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
struct timeval ast_mktime(struct ast_tm *const tmp, const char *zone)
Timezone-independent version of mktime(3).
Definition: localtime.c:2357
int ast_say_number(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options)
says a number
Definition: channel.c:8235
Standard Command Line Interface.
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:407
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:80
ast_app: A registered application
Definition: pbx_app.c:45
static int audio_buffers
The number of audio buffers to be allocated on pseudo channels when in a conference.
Definition: app_meetme.c:928
int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
Writes a frame to a stream.
Definition: file.c:244
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1840
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1129
char pinadmin[MAX_PIN]
Definition: app_meetme.c:874
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2805
long play_warning
Definition: app_meetme.c:917
Data structure associated with a single frame of data.
Internal Asterisk hangup causes.
Abstract JSON element (object, array, string, int, ...).
static int admin_exec(struct ast_channel *chan, const char *data)
The MeetMeAdmin application.
Definition: app_meetme.c:4777
#define AST_OPTION_TONE_VERIFY
struct ast_channel_id uniqueid
Definition: search.h:40
struct timeval ast_tvsub(struct timeval a, struct timeval b)
Returns the difference of two timevals a - b.
Definition: extconf.c:2297
char * recordingfilename
Definition: app_meetme.c:871
struct ast_channel * lchan
Definition: app_meetme.c:855
enum ast_frame_type frametype
int ast_frame_adjust_volume(struct ast_frame *f, int adjustment)
Adjusts the volume of the audio samples contained in a frame.
Definition: main/frame.c:787
Generic container type.
unsigned char valid
TRUE if the name information is valid/present.
Definition: channel.h:279
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
The MeetMe Conference object.
Definition: app_meetme.c:850
struct ast_format * format
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
struct ast_app * pbx_findapp(const char *app)
Look up an application.
Definition: ael_main.c:165
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:191
Say numbers and dates (maybe words one day too)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
#define manager_event(category, event, contents,...)
External routines may send asterisk manager events this way.
Definition: manager.h:253
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
Definition: dsp.c:2009
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:3389
Asterisk module definitions.
intmax_t ast_json_integer_get(const struct ast_json *integer)
Get the value from a JSON integer.
Definition: json.c:332
int ast_str_append_event_header(struct ast_str **fields_string, const char *header, const char *value)
append an event header to an ast string
Definition: manager.c:10541
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
executes a write operation on a function
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
struct ast_frame * ast_read_noaudio(struct ast_channel *chan)
Reads a frame, returning AST_FRAME_NULL frame if audio.
Definition: channel.c:4267
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:297
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
struct ast_channel * chan
Definition: app_meetme.c:908
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
static int channel_admin_exec(struct ast_channel *chan, const char *data)
The MeetMeChannelAdmin application MeetMeChannelAdmin(channel, command)
Definition: app_meetme.c:4950
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:222
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
Structure for mutex and tracking information.
Definition: lock.h:135
void ast_translator_free_path(struct ast_trans_pvt *tr)
Frees a translator path Frees the given translator path structure.
Definition: translate.c:476
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
ast_mutex_t recordthreadlock
Definition: app_meetme.c:869
#define AST_APP_ARG(name)
Define an application argument.
#define AST_LIST_APPEND_LIST(head, list, field)
Appends a whole list to the tail of a list.
Definition: linkedlists.h:783
char confno[MAX_CONFNUM]
Definition: app_meetme.c:853
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: utils.c:2479
void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
Send ack in manager transaction to begin a list.
Definition: manager.c:3431
struct ast_json * ast_json_integer_create(intmax_t value)
Create a JSON integer.
Definition: json.c:327
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:342
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532