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