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