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