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