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