Asterisk - The Open Source Telephony Project  21.4.1
app_confbridge.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2007-2008, Digium, Inc.
5  *
6  * Joshua Colp <jcolp@digium.com>
7  * David Vossel <dvossel@digium.com>
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19 
20 /*! \file
21  *
22  * \brief Conference Bridge application
23  *
24  * \author\verbatim Joshua Colp <jcolp@digium.com> \endverbatim
25  * \author\verbatim David Vossel <dvossel@digium.com> \endverbatim
26  *
27  * This is a conference bridge application utilizing the bridging core.
28  * \ingroup applications
29  */
30 
31 /*! \li \ref app_confbridge.c uses the configuration file \ref confbridge.conf
32  * \addtogroup configuration_file Configuration Files
33  */
34 
35 /*!
36  * \page confbridge.conf confbridge.conf
37  * \verbinclude confbridge.conf.sample
38  */
39 
40 /*** MODULEINFO
41  <support_level>core</support_level>
42  ***/
43 
44 #include "asterisk.h"
45 
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49 #include <string.h>
50 #include <signal.h>
51 
52 #include "asterisk/cli.h"
53 #include "asterisk/file.h"
54 #include "asterisk/channel.h"
55 #include "asterisk/pbx.h"
56 #include "asterisk/pbx.h"
57 #include "asterisk/module.h"
58 #include "asterisk/lock.h"
59 #include "asterisk/bridge.h"
60 #include "asterisk/musiconhold.h"
61 #include "asterisk/say.h"
62 #include "asterisk/audiohook.h"
63 #include "asterisk/astobj2.h"
64 #include "confbridge/include/confbridge.h"
65 #include "asterisk/paths.h"
66 #include "asterisk/manager.h"
67 #include "asterisk/test.h"
68 #include "asterisk/stasis.h"
69 #include "asterisk/stasis_bridges.h"
70 #include "asterisk/stasis_channels.h"
71 #include "asterisk/json.h"
72 #include "asterisk/format_cache.h"
73 #include "asterisk/taskprocessor.h"
74 #include "asterisk/stream.h"
75 #include "asterisk/message.h"
76 
77 /*** DOCUMENTATION
78  <application name="ConfBridge" language="en_US">
79  <synopsis>
80  Conference bridge application.
81  </synopsis>
82  <syntax>
83  <parameter name="conference" required="true">
84  <para>Name of the conference bridge. You are not limited to just
85  numbers.</para>
86  </parameter>
87  <parameter name="bridge_profile">
88  <para>The bridge profile name from confbridge.conf. When left blank,
89  a dynamically built bridge profile created by the CONFBRIDGE dialplan
90  function is searched for on the channel and used. If no dynamic
91  profile is present, the 'default_bridge' profile found in
92  confbridge.conf is used. </para>
93  <para>It is important to note that while user profiles may be unique
94  for each participant, mixing bridge profiles on a single conference
95  is _NOT_ recommended and will produce undefined results.</para>
96  </parameter>
97  <parameter name="user_profile">
98  <para>The user profile name from confbridge.conf. When left blank,
99  a dynamically built user profile created by the CONFBRIDGE dialplan
100  function is searched for on the channel and used. If no dynamic
101  profile is present, the 'default_user' profile found in
102  confbridge.conf is used.</para>
103  </parameter>
104  <parameter name="menu">
105  <para>The name of the DTMF menu in confbridge.conf to be applied to
106  this channel. When left blank, a dynamically built menu profile
107  created by the CONFBRIDGE dialplan function is searched for on
108  the channel and used. If no dynamic profile is present, the
109  'default_menu' profile found in confbridge.conf is used.</para>
110  </parameter>
111  </syntax>
112  <description>
113  <para>Enters the user into a specified conference bridge. The user can
114  exit the conference by hangup or DTMF menu option.</para>
115  <para>This application sets the following channel variable upon completion:</para>
116  <variablelist>
117  <variable name="CONFBRIDGE_RESULT">
118  <value name="FAILED">The channel encountered an error and could not enter the conference.</value>
119  <value name="HANGUP">The channel exited the conference by hanging up.</value>
120  <value name="KICKED">The channel was kicked from the conference.</value>
121  <value name="ENDMARKED">The channel left the conference as a result of the last marked user leaving.</value>
122  <value name="DTMF">The channel pressed a DTMF sequence to exit the conference.</value>
123  <value name="TIMEOUT">The channel reached its configured timeout.</value>
124  </variable>
125  </variablelist>
126  </description>
127  <see-also>
128  <ref type="application">ConfKick</ref>
129  <ref type="function">CONFBRIDGE</ref>
130  <ref type="function">CONFBRIDGE_INFO</ref>
131  <ref type="function">CONFBRIDGE_CHANNELS</ref>
132  </see-also>
133  </application>
134  <application name="ConfKick" language="en_US">
135  <since>
136  <version>16.19.0</version>
137  <version>18.5.0</version>
138  <version>19.0.0</version>
139  </since>
140  <synopsis>
141  Kicks channel(s) from the requested ConfBridge.
142  </synopsis>
143  <syntax>
144  <parameter name="conference" required="true" />
145  <parameter name="channel">
146  <para>The channel to kick, <literal>all</literal>
147  to kick all users, or <literal>participants</literal>
148  to kick all non-admin participants. Default is all.</para>
149  </parameter>
150  </syntax>
151  <description>
152  <para>Kicks the requested channel(s) from a conference bridge.</para>
153  <variablelist>
154  <variable name="CONFKICKSTATUS">
155  <value name="FAILURE">
156  Could not kick any users with the provided arguments.
157  </value>
158  <value name="SUCCESS">
159  Successfully kicked users from specified conference bridge.
160  </value>
161  </variable>
162  </variablelist>
163  </description>
164  <see-also>
165  <ref type="application">ConfBridge</ref>
166  <ref type="function">CONFBRIDGE</ref>
167  <ref type="function">CONFBRIDGE_INFO</ref>
168  <ref type="function">CONFBRIDGE_CHANNELS</ref>
169  </see-also>
170  </application>
171  <function name="CONFBRIDGE" language="en_US">
172  <synopsis>
173  Set a custom dynamic bridge, user, or menu profile on a channel for the
174  ConfBridge application using the same options available in confbridge.conf.
175  </synopsis>
176  <syntax>
177  <parameter name="type" required="true">
178  <para>To what type of conference profile the option applies.</para>
179  <enumlist>
180  <enum name="bridge"></enum>
181  <enum name="menu"></enum>
182  <enum name="user"></enum>
183  </enumlist>
184  </parameter>
185  <parameter name="option" required="true">
186  <para>Option refers to a <filename>confbridge.conf</filename> option
187  that is being set dynamically on this channel, or <literal>clear</literal>
188  to remove already applied profile options from the channel.</para>
189  </parameter>
190  </syntax>
191  <description>
192  <para>A custom profile uses the default profile type settings defined in
193  <filename>confbridge.conf</filename> as defaults if the profile template
194  is not explicitly specified first.</para>
195  <para>For <literal>bridge</literal> profiles the default template is <literal>default_bridge</literal>.</para>
196  <para>For <literal>menu</literal> profiles the default template is <literal>default_menu</literal>.</para>
197  <para>For <literal>user</literal> profiles the default template is <literal>default_user</literal>.</para>
198  <para>---- Example 1 ----</para>
199  <para>In this example the custom user profile set on the channel will
200  automatically be used by the ConfBridge application.</para>
201  <example title="Example 1">
202  exten => 1,1,Answer()
203  </example>
204  <para>; In this example the effect of the following line is</para>
205  <para>; implied:</para>
206  <example title="Example 1b">
207  same => n,Set(CONFBRIDGE(user,template)=default_user)
208  same => n,Set(CONFBRIDGE(user,announce_join_leave)=yes)
209  same => n,Set(CONFBRIDGE(user,startmuted)=yes)
210  same => n,ConfBridge(1)
211  </example>
212  <para>---- Example 2 ----</para>
213  <para>This example shows how to use a predefined user profile in
214  <filename>confbridge.conf</filename> as a template for a dynamic profile.
215  Here we make an admin/marked user out of the <literal>my_user</literal>
216  profile that you define in <filename>confbridge.conf</filename>.</para>
217  <example title="Example 2">
218  exten => 1,1,Answer()
219  same => n,Set(CONFBRIDGE(user,template)=my_user)
220  same => n,Set(CONFBRIDGE(user,admin)=yes)
221  same => n,Set(CONFBRIDGE(user,marked)=yes)
222  same => n,ConfBridge(1)
223  </example>
224  </description>
225  </function>
226  <function name="CONFBRIDGE_INFO" language="en_US">
227  <synopsis>
228  Get information about a ConfBridge conference.
229  </synopsis>
230  <syntax>
231  <parameter name="type" required="true">
232  <para>What conference information is requested.</para>
233  <enumlist>
234  <enum name="admins">
235  <para>Get the number of admin users in the conference.</para>
236  </enum>
237  <enum name="locked">
238  <para>Determine if the conference is locked. (0 or 1)</para>
239  </enum>
240  <enum name="marked">
241  <para>Get the number of marked users in the conference.</para>
242  </enum>
243  <enum name="muted">
244  <para>Determine if the conference is muted. (0 or 1)</para>
245  </enum>
246  <enum name="parties">
247  <para>Get the number of users in the conference.</para>
248  </enum>
249  </enumlist>
250  </parameter>
251  <parameter name="conf" required="true">
252  <para>The name of the conference being referenced.</para>
253  </parameter>
254  </syntax>
255  <description>
256  <para>This function returns a non-negative integer for valid conference
257  names and an empty string for invalid conference names.</para>
258  </description>
259  <see-also>
260  <ref type="function">CONFBRIDGE_CHANNELS</ref>
261  </see-also>
262  </function>
263  <function name="CONFBRIDGE_CHANNELS" language="en_US">
264  <since>
265  <version>16.26.0</version>
266  <version>18.12.0</version>
267  <version>19.4.0</version>
268  </since>
269  <synopsis>
270  Get a list of channels in a ConfBridge conference.
271  </synopsis>
272  <syntax>
273  <parameter name="type" required="true">
274  <para>What conference information is requested.</para>
275  <enumlist>
276  <enum name="admins">
277  <para>Get the number of admin users in the conference.</para>
278  </enum>
279  <enum name="marked">
280  <para>Get the number of marked users in the conference.</para>
281  </enum>
282  <enum name="parties">
283  <para>Get the number of total users in the conference.</para>
284  </enum>
285  <enum name="active">
286  <para>Get the number of active users in the conference.</para>
287  </enum>
288  <enum name="waiting">
289  <para>Get the number of waiting users in the conference.</para>
290  </enum>
291  </enumlist>
292  </parameter>
293  <parameter name="conf" required="true">
294  <para>The name of the conference being referenced.</para>
295  </parameter>
296  </syntax>
297  <description>
298  <para>This function returns a comma-separated list of channels in a ConfBridge conference, optionally filtered by a type of participant.</para>
299  </description>
300  <see-also>
301  <ref type="function">CONFBRIDGE_INFO</ref>
302  </see-also>
303  </function>
304  <manager name="ConfbridgeList" language="en_US">
305  <synopsis>
306  List participants in a conference.
307  </synopsis>
308  <syntax>
309  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
310  <parameter name="Conference" required="true">
311  <para>Conference number.</para>
312  </parameter>
313  </syntax>
314  <description>
315  <para>Lists all users in a particular ConfBridge conference.
316  ConfbridgeList will follow as separate events, followed by a final event called
317  ConfbridgeListComplete.</para>
318  </description>
319  </manager>
320  <managerEvent language="en_US" name="ConfbridgeList">
321  <managerEventInstance class="EVENT_FLAG_REPORTING">
322  <synopsis>Raised as part of the ConfbridgeList action response list.</synopsis>
323  <syntax>
324  <parameter name="Conference">
325  <para>The name of the Confbridge conference.</para>
326  </parameter>
327  <parameter name="Admin">
328  <para>Identifies this user as an admin user.</para>
329  <enumlist>
330  <enum name="Yes"/>
331  <enum name="No"/>
332  </enumlist>
333  </parameter>
334  <parameter name="MarkedUser">
335  <para>Identifies this user as a marked user.</para>
336  <enumlist>
337  <enum name="Yes"/>
338  <enum name="No"/>
339  </enumlist>
340  </parameter>
341  <parameter name="WaitMarked">
342  <para>Must this user wait for a marked user to join?</para>
343  <enumlist>
344  <enum name="Yes"/>
345  <enum name="No"/>
346  </enumlist>
347  </parameter>
348  <parameter name="EndMarked">
349  <para>Does this user get kicked after the last marked user leaves?</para>
350  <enumlist>
351  <enum name="Yes"/>
352  <enum name="No"/>
353  </enumlist>
354  </parameter>
355  <parameter name="Waiting">
356  <para>Is this user waiting for a marked user to join?</para>
357  <enumlist>
358  <enum name="Yes"/>
359  <enum name="No"/>
360  </enumlist>
361  </parameter>
362  <parameter name="Muted">
363  <para>The current mute status.</para>
364  <enumlist>
365  <enum name="Yes"/>
366  <enum name="No"/>
367  </enumlist>
368  </parameter>
369  <parameter name="Talking">
370  <para>Is this user talking?</para>
371  <enumlist>
372  <enum name="Yes"/>
373  <enum name="No"/>
374  </enumlist>
375  </parameter>
376  <parameter name="AnsweredTime">
377  <para>The number of seconds the channel has been up.</para>
378  </parameter>
379  <channel_snapshot/>
380  </syntax>
381  </managerEventInstance>
382  </managerEvent>
383  <manager name="ConfbridgeListRooms" language="en_US">
384  <synopsis>
385  List active conferences.
386  </synopsis>
387  <syntax>
388  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
389  </syntax>
390  <description>
391  <para>Lists data about all active conferences.
392  ConfbridgeListRooms will follow as separate events, followed by a final event called
393  ConfbridgeListRoomsComplete.</para>
394  </description>
395  </manager>
396  <managerEvent language="en_US" name="ConfbridgeListRooms">
397  <managerEventInstance class="EVENT_FLAG_REPORTING">
398  <synopsis>Raised as part of the ConfbridgeListRooms action response list.</synopsis>
399  <syntax>
400  <parameter name="Conference">
401  <para>The name of the Confbridge conference.</para>
402  </parameter>
403  <parameter name="Parties">
404  <para>Number of users in the conference.</para>
405  <para>This includes both active and waiting users.</para>
406  </parameter>
407  <parameter name="Marked">
408  <para>Number of marked users in the conference.</para>
409  </parameter>
410  <parameter name="Locked">
411  <para>Is the conference locked?</para>
412  <enumlist>
413  <enum name="Yes"/>
414  <enum name="No"/>
415  </enumlist>
416  </parameter>
417  <parameter name="Muted">
418  <para>Is the conference muted?</para>
419  <enumlist>
420  <enum name="Yes"/>
421  <enum name="No"/>
422  </enumlist>
423  </parameter>
424  </syntax>
425  </managerEventInstance>
426  </managerEvent>
427  <manager name="ConfbridgeMute" language="en_US">
428  <synopsis>
429  Mute a Confbridge user.
430  </synopsis>
431  <syntax>
432  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
433  <parameter name="Conference" required="true" />
434  <parameter name="Channel" required="true">
435  <para>If this parameter is not a complete channel name, the first channel with this prefix will be used.</para>
436  <para>If this parameter is "all", all channels will be muted.</para>
437  <para>If this parameter is "participants", all non-admin channels will be muted.</para>
438  </parameter>
439  </syntax>
440  <description>
441  </description>
442  </manager>
443  <manager name="ConfbridgeUnmute" language="en_US">
444  <synopsis>
445  Unmute a Confbridge user.
446  </synopsis>
447  <syntax>
448  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
449  <parameter name="Conference" required="true" />
450  <parameter name="Channel" required="true">
451  <para>If this parameter is not a complete channel name, the first channel with this prefix will be used.</para>
452  <para>If this parameter is "all", all channels will be unmuted.</para>
453  <para>If this parameter is "participants", all non-admin channels will be unmuted.</para>
454  </parameter>
455  </syntax>
456  <description>
457  </description>
458  </manager>
459  <manager name="ConfbridgeKick" language="en_US">
460  <synopsis>
461  Kick a Confbridge user.
462  </synopsis>
463  <syntax>
464  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
465  <parameter name="Conference" required="true" />
466  <parameter name="Channel" required="true" >
467  <para>If this parameter is "all", all channels will be kicked from the conference.</para>
468  <para>If this parameter is "participants", all non-admin channels will be kicked from the conference.</para>
469  </parameter>
470  </syntax>
471  <description>
472  </description>
473  </manager>
474  <manager name="ConfbridgeLock" language="en_US">
475  <synopsis>
476  Lock a Confbridge conference.
477  </synopsis>
478  <syntax>
479  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
480  <parameter name="Conference" required="true" />
481  </syntax>
482  <description>
483  </description>
484  </manager>
485  <manager name="ConfbridgeUnlock" language="en_US">
486  <synopsis>
487  Unlock a Confbridge conference.
488  </synopsis>
489  <syntax>
490  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
491  <parameter name="Conference" required="true" />
492  </syntax>
493  <description>
494  </description>
495  </manager>
496  <manager name="ConfbridgeStartRecord" language="en_US">
497  <synopsis>
498  Start recording a Confbridge conference.
499  </synopsis>
500  <syntax>
501  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
502  <parameter name="Conference" required="true" />
503  <parameter name="RecordFile" required="false" />
504  </syntax>
505  <description>
506  <para>Start recording a conference. If recording is already present an error will be returned. If RecordFile is not provided, the default record file specified in the conference's bridge profile will be used, if that is not present either a file will automatically be generated in the monitor directory.</para>
507  </description>
508  </manager>
509  <manager name="ConfbridgeStopRecord" language="en_US">
510  <synopsis>
511  Stop recording a Confbridge conference.
512  </synopsis>
513  <syntax>
514  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
515  <parameter name="Conference" required="true" />
516  </syntax>
517  <description>
518  </description>
519  </manager>
520  <manager name="ConfbridgeSetSingleVideoSrc" language="en_US">
521  <synopsis>
522  Set a conference user as the single video source distributed to all other participants.
523  </synopsis>
524  <syntax>
525  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
526  <parameter name="Conference" required="true" />
527  <parameter name="Channel" required="true">
528  <para>If this parameter is not a complete channel name, the first channel with this prefix will be used.</para>
529  </parameter>
530  </syntax>
531  <description>
532  </description>
533  </manager>
534 
535 ***/
536 
537 /*!
538  * \par Playing back a file to a channel in a conference
539  * You might notice in this application that while playing a sound file
540  * to a channel the actual conference bridge lock is not held. This is done so
541  * that other channels are not blocked from interacting with the conference bridge.
542  * Unfortunately because of this it is possible for things to change after the sound file
543  * is done being played. Data must therefore be checked after reacquiring the conference
544  * bridge lock if it is important.
545  */
546 
547 static const char app[] = "ConfBridge";
548 static const char app2[] = "ConfKick";
549 
550 /*! Number of buckets our conference bridges container can have */
551 #define CONFERENCE_BRIDGE_BUCKETS 53
552 
553 /*! Initial recording filename space. */
554 #define RECORD_FILENAME_INITIAL_SPACE 128
555 
556 /*! \brief Container to hold all conference bridges in progress */
558 
559 static void leave_conference(struct confbridge_user *user);
560 static int play_sound_number(struct confbridge_conference *conference, int say_number);
561 static int execute_menu_entry(struct confbridge_conference *conference,
562  struct confbridge_user *user,
563  struct ast_bridge_channel *bridge_channel,
564  struct conf_menu_entry *menu_entry,
565  struct conf_menu *menu);
566 
567 /*! \brief Hashing function used for conference bridges container */
568 static int conference_bridge_hash_cb(const void *obj, const int flags)
569 {
570  const struct confbridge_conference *conference = obj;
571  const char *name = obj;
572  int hash;
573 
574  switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
575  default:
576  case OBJ_POINTER:
577  name = conference->name;
578  /* Fall through */
579  case OBJ_KEY:
580  hash = ast_str_case_hash(name);
581  break;
582  case OBJ_PARTIAL_KEY:
583  /* Should never happen in hash callback. */
584  ast_assert(0);
585  hash = 0;
586  break;
587  }
588  return hash;
589 }
590 
591 /*! \brief Comparison function used for conference bridges container */
592 static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
593 {
594  const struct confbridge_conference *left = obj;
595  const struct confbridge_conference *right = arg;
596  const char *right_name = arg;
597  int cmp;
598 
599  switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
600  default:
601  case OBJ_POINTER:
602  right_name = right->name;
603  /* Fall through */
604  case OBJ_KEY:
605  cmp = strcasecmp(left->name, right_name);
606  break;
607  case OBJ_PARTIAL_KEY:
608  cmp = strncasecmp(left->name, right_name, strlen(right_name));
609  break;
610  }
611  return cmp ? 0 : CMP_MATCH;
612 }
613 
614 const char *conf_get_sound(enum conf_sounds sound, struct bridge_profile_sounds *custom_sounds)
615 {
616  switch (sound) {
617  case CONF_SOUND_HAS_JOINED:
618  return S_OR(custom_sounds->hasjoin, "conf-hasjoin");
619  case CONF_SOUND_HAS_LEFT:
620  return S_OR(custom_sounds->hasleft, "conf-hasleft");
621  case CONF_SOUND_KICKED:
622  return S_OR(custom_sounds->kicked, "conf-kicked");
623  case CONF_SOUND_MUTED:
624  return S_OR(custom_sounds->muted, "conf-muted");
625  case CONF_SOUND_UNMUTED:
626  return S_OR(custom_sounds->unmuted, "conf-unmuted");
627  case CONF_SOUND_BINAURAL_ON:
628  return S_OR(custom_sounds->binauralon, "confbridge-binaural-on");
629  case CONF_SOUND_BINAURAL_OFF:
630  return S_OR(custom_sounds->binauraloff, "confbridge-binaural-off");
631  case CONF_SOUND_ONLY_ONE:
632  return S_OR(custom_sounds->onlyone, "conf-onlyone");
633  case CONF_SOUND_THERE_ARE:
634  return S_OR(custom_sounds->thereare, "conf-thereare");
635  case CONF_SOUND_OTHER_IN_PARTY:
636  return S_OR(custom_sounds->otherinparty, "conf-otherinparty");
637  case CONF_SOUND_PLACE_IN_CONF:
638  return S_OR(custom_sounds->placeintoconf, "conf-placeintoconf");
639  case CONF_SOUND_WAIT_FOR_LEADER:
640  return S_OR(custom_sounds->waitforleader, "conf-waitforleader");
641  case CONF_SOUND_LEADER_HAS_LEFT:
642  return S_OR(custom_sounds->leaderhasleft, "conf-leaderhasleft");
643  case CONF_SOUND_GET_PIN:
644  return S_OR(custom_sounds->getpin, "conf-getpin");
645  case CONF_SOUND_INVALID_PIN:
646  return S_OR(custom_sounds->invalidpin, "conf-invalidpin");
647  case CONF_SOUND_ONLY_PERSON:
648  return S_OR(custom_sounds->onlyperson, "conf-onlyperson");
649  case CONF_SOUND_LOCKED:
650  return S_OR(custom_sounds->locked, "conf-locked");
651  case CONF_SOUND_LOCKED_NOW:
652  return S_OR(custom_sounds->lockednow, "conf-lockednow");
653  case CONF_SOUND_UNLOCKED_NOW:
654  return S_OR(custom_sounds->unlockednow, "conf-unlockednow");
655  case CONF_SOUND_ERROR_MENU:
656  return S_OR(custom_sounds->errormenu, "conf-errormenu");
657  case CONF_SOUND_JOIN:
658  return S_OR(custom_sounds->join, "confbridge-join");
659  case CONF_SOUND_LEAVE:
660  return S_OR(custom_sounds->leave, "confbridge-leave");
661  case CONF_SOUND_PARTICIPANTS_MUTED:
662  return S_OR(custom_sounds->participantsmuted, "conf-now-muted");
663  case CONF_SOUND_PARTICIPANTS_UNMUTED:
664  return S_OR(custom_sounds->participantsunmuted, "conf-now-unmuted");
665  case CONF_SOUND_BEGIN:
666  return S_OR(custom_sounds->begin, "confbridge-conf-begin");
667  }
668 
669  return "";
670 }
671 
672 
673 static void send_conf_stasis(struct confbridge_conference *conference, struct ast_channel *chan,
674  struct stasis_message_type *type, struct ast_json *extras, int channel_topic)
675 {
676  RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
677  RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
678 
679  json_object = ast_json_pack("{s: s}",
680  "conference", conference->name);
681  if (!json_object) {
682  return;
683  }
684 
685  if (extras) {
686  ast_json_object_update(json_object, extras);
687  }
688 
689  ast_bridge_lock(conference->bridge);
690  msg = ast_bridge_blob_create(type,
691  conference->bridge,
692  chan,
693  json_object);
694  ast_bridge_unlock(conference->bridge);
695  if (!msg) {
696  return;
697  }
698 
699  if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_ENABLE_EVENTS)) {
700  conf_send_event_to_participants(conference, chan, msg);
701  }
702 
703  if (channel_topic) {
704  stasis_publish(ast_channel_topic(chan), msg);
705  } else {
706  stasis_publish(ast_bridge_topic(conference->bridge), msg);
707  }
708 }
709 
710 static void send_conf_stasis_snapshots(struct confbridge_conference *conference,
711  struct ast_channel_snapshot *chan_snapshot, struct stasis_message_type *type,
712  struct ast_json *extras)
713 {
714  RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
715  RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
716  RAII_VAR(struct ast_bridge_snapshot *, bridge_snapshot, NULL, ao2_cleanup);
717 
718  json_object = ast_json_pack("{s: s}",
719  "conference", conference->name);
720  if (!json_object) {
721  return;
722  }
723 
724  if (extras) {
725  ast_json_object_update(json_object, extras);
726  }
727 
728  ast_bridge_lock(conference->bridge);
729  bridge_snapshot = ast_bridge_snapshot_create(conference->bridge);
730  ast_bridge_unlock(conference->bridge);
731  if (!bridge_snapshot) {
732  return;
733  }
734 
736  bridge_snapshot,
737  chan_snapshot,
738  json_object);
739  if (!msg) {
740  return;
741  }
742 
743  stasis_publish(ast_bridge_topic(conference->bridge), msg);
744 }
745 
746 
747 static void send_conf_start_event(struct confbridge_conference *conference)
748 {
749  send_conf_stasis(conference, NULL, confbridge_start_type(), NULL, 0);
750 }
751 
752 static void send_conf_end_event(struct confbridge_conference *conference)
753 {
754  send_conf_stasis(conference, NULL, confbridge_end_type(), NULL, 0);
755 }
756 
757 static void send_join_event(struct confbridge_user *user, struct confbridge_conference *conference)
758 {
759  struct ast_json *json_object;
760 
761  json_object = ast_json_pack("{s: b, s: b}",
762  "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN),
763  "muted", user->muted);
764  if (!json_object) {
765  return;
766  }
767  send_conf_stasis(conference, user->chan, confbridge_join_type(), json_object, 0);
768  ast_json_unref(json_object);
769 }
770 
771 static void send_leave_event(struct confbridge_user *user, struct confbridge_conference *conference)
772 {
773  struct ast_json *json_object;
774 
775  json_object = ast_json_pack("{s: b}",
776  "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN)
777  );
778  if (!json_object) {
779  return;
780  }
781  send_conf_stasis(conference, user->chan, confbridge_leave_type(), json_object, 0);
782  ast_json_unref(json_object);
783 }
784 
785 static void send_start_record_event(struct confbridge_conference *conference)
786 {
787  send_conf_stasis(conference, NULL, confbridge_start_record_type(), NULL, 0);
788 }
789 
790 static void send_stop_record_event(struct confbridge_conference *conference)
791 {
792  send_conf_stasis(conference, NULL, confbridge_stop_record_type(), NULL, 0);
793 }
794 
795 static void send_mute_event(struct confbridge_user *user, struct confbridge_conference *conference)
796 {
797  struct ast_json *json_object;
798 
799  json_object = ast_json_pack("{s: b}",
800  "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN)
801  );
802  if (!json_object) {
803  return;
804  }
805  send_conf_stasis(conference, user->chan, confbridge_mute_type(), json_object, 1);
806  ast_json_unref(json_object);
807 }
808 
809 static void send_unmute_event(struct confbridge_user *user, struct confbridge_conference *conference)
810 {
811  struct ast_json *json_object;
812 
813  json_object = ast_json_pack("{s: b}",
814  "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN)
815  );
816  if (!json_object) {
817  return;
818  }
819  send_conf_stasis(conference, user->chan, confbridge_unmute_type(), json_object, 1);
820  ast_json_unref(json_object);
821 }
822 
823 static void set_rec_filename(struct confbridge_conference *conference, struct ast_str **filename, int is_new)
824 {
825  char *rec_file = conference->b_profile.rec_file;
826  char *ext;
827  time_t now;
828 
829  if (ast_str_strlen(*filename)
830  && ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_FILE_APPEND)
831  && !is_new) {
832  return;
833  }
834 
835  time(&now);
836 
837  ast_str_reset(*filename);
838  if (ast_strlen_zero(rec_file)) {
839  ast_str_set(filename, 0, "confbridge-%s-%u.wav", conference->name,
840  (unsigned int) now);
841  } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_FILE_TIMESTAMP)) {
842  /* insert time before file extension */
843  ext = strrchr(rec_file, '.');
844  if (ext) {
845  ast_str_set_substr(filename, 0, rec_file, ext - rec_file);
846  ast_str_append(filename, 0, "-%u%s", (unsigned int) now, ext);
847  } else {
848  ast_str_set(filename, 0, "%s-%u", rec_file, (unsigned int) now);
849  }
850  } else {
851  ast_str_set(filename, 0, "%s", rec_file);
852  }
853  ast_str_append(filename, 0, ",%s%s,%s",
854  ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_FILE_APPEND) ? "a" : "",
855  conference->b_profile.rec_options,
856  conference->b_profile.rec_command);
857 }
858 
859 static int is_new_rec_file(const char *rec_file, struct ast_str **orig_rec_file)
860 {
861  if (!ast_strlen_zero(rec_file)) {
862  if (!*orig_rec_file) {
864  }
865 
866  if (*orig_rec_file
867  && strcmp(ast_str_buffer(*orig_rec_file), rec_file)) {
868  ast_str_set(orig_rec_file, 0, "%s", rec_file);
869  return 1;
870  }
871  }
872  return 0;
873 }
874 
875 struct confbridge_conference *conf_find_bridge(const char *conference_name)
876 {
877  return ao2_find(conference_bridges, conference_name, OBJ_KEY);
878 }
879 
880 /*!
881  * \internal
882  * \brief Returns whether or not conference is being recorded.
883  *
884  * \param conference The bridge to check for recording
885  *
886  * \note Must be called with the conference locked
887  *
888  * \retval 1 conference is recording.
889  * \retval 0 conference is NOT recording.
890  */
891 static int conf_is_recording(struct confbridge_conference *conference)
892 {
893  return conference->record_chan != NULL;
894 }
895 
896 /*!
897  * \internal
898  * \brief Stop recording a conference bridge
899  *
900  * \param conference The conference bridge on which to stop the recording
901  *
902  * \note Must be called with the conference locked
903  *
904  * \retval -1 Failure
905  * \retval 0 Success
906  */
907 static int conf_stop_record(struct confbridge_conference *conference)
908 {
909  struct ast_channel *chan;
910  struct ast_frame f = { AST_FRAME_CONTROL, .subclass.integer = AST_CONTROL_HANGUP };
911 
912  if (!conf_is_recording(conference)) {
913  return -1;
914  }
915 
916  /* Remove the recording channel from the conference bridge. */
917  chan = conference->record_chan;
918  conference->record_chan = NULL;
919  ast_queue_frame(chan, &f);
920  ast_channel_unref(chan);
921 
922  ast_test_suite_event_notify("CONF_STOP_RECORD", "Message: stopped conference recording channel\r\nConference: %s", conference->b_profile.name);
923  send_stop_record_event(conference);
924 
925  return 0;
926 }
927 
928 /*!
929  * \internal
930  * \brief Start recording the conference
931  *
932  * \param conference The conference bridge to start recording
933  *
934  * \note Must be called with the conference locked
935  *
936  * \retval 0 success
937  * \retval non-zero failure
938  */
939 static int conf_start_record(struct confbridge_conference *conference)
940 {
941  struct ast_app *mixmonapp;
942  struct ast_channel *chan;
943  struct ast_format_cap *cap;
944  struct ast_bridge_features *features;
945 
946  if (conf_is_recording(conference)) {
947  return -1;
948  }
949 
950  mixmonapp = pbx_findapp("MixMonitor");
951  if (!mixmonapp) {
952  ast_log(LOG_WARNING, "Cannot record ConfBridge, MixMonitor app is not installed\n");
953  return -1;
954  }
955 
956  features = ast_bridge_features_new();
957  if (!features) {
958  return -1;
959  }
960  ast_set_flag(&features->feature_flags, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE);
961 
963  if (!cap) {
964  ast_bridge_features_destroy(features);
965  return -1;
966  }
968 
969  /* Create the recording channel. */
970  chan = ast_request("CBRec", cap, NULL, NULL, conference->name, NULL);
971  ao2_ref(cap, -1);
972  if (!chan) {
973  ast_bridge_features_destroy(features);
974  return -1;
975  }
976 
977  /* Start recording. */
978  set_rec_filename(conference, &conference->record_filename,
979  is_new_rec_file(conference->b_profile.rec_file, &conference->orig_rec_file));
980  ast_answer(chan);
981  pbx_exec(chan, mixmonapp, ast_str_buffer(conference->record_filename));
982 
983  /* Put the channel into the conference bridge. */
984  ast_channel_ref(chan);
985  conference->record_chan = chan;
986  if (ast_bridge_impart(conference->bridge, chan, NULL, features,
988  ast_hangup(chan);
989  ast_channel_unref(chan);
990  conference->record_chan = NULL;
991  return -1;
992  }
993 
994  ast_test_suite_event_notify("CONF_START_RECORD", "Message: started conference recording channel\r\nConference: %s", conference->b_profile.name);
995  send_start_record_event(conference);
996 
997  return 0;
998 }
999 
1000 /*! \brief Playback the given filename and monitor for any dtmf interrupts.
1001  *
1002  * This function is used to playback sound files on a given channel and optionally
1003  * allow dtmf interrupts to occur.
1004  *
1005  * If the optional bridge_channel parameter is given then sound file playback
1006  * is played on that channel and dtmf interruptions are allowed. However, if
1007  * bridge_channel is not set then the channel parameter is expected to be set
1008  * instead and non interruptible playback is played on that channel.
1009  *
1010  * \param bridge_channel Bridge channel to play file on
1011  * \param channel Optional channel to play file on if bridge_channel not given
1012  * \param filename The file name to playback
1013  *
1014  * \retval -1 failure during playback.
1015  * \retval 0 on file was fully played.
1016  * \retval 1 on dtmf interrupt.
1017  */
1018 static int play_file(struct ast_bridge_channel *bridge_channel, struct ast_channel *channel,
1019  const char *filename)
1020 {
1021  struct ast_channel *chan;
1022  const char *stop_digits;
1023  int digit;
1024 
1025  if (bridge_channel) {
1026  chan = bridge_channel->chan;
1027  stop_digits = AST_DIGIT_ANY;
1028  } else {
1029  chan = channel;
1030  stop_digits = AST_DIGIT_NONE;
1031  }
1032 
1033  digit = ast_stream_and_wait(chan, filename, stop_digits);
1034  if (digit < 0) {
1035  ast_log(LOG_WARNING, "Failed to playback file '%s' to channel\n", filename);
1036  return -1;
1037  }
1038 
1039  if (digit > 0) {
1040  ast_stopstream(bridge_channel->chan);
1041  ast_bridge_channel_feature_digit_add(bridge_channel, digit);
1042  return 1;
1043  }
1044 
1045  return 0;
1046 }
1047 
1048 /*!
1049  * \internal
1050  * \brief Complain if the given sound file does not exist.
1051  *
1052  * \param filename Sound file to check if exists.
1053  *
1054  * \retval non-zero if the file exists.
1055  */
1056 static int sound_file_exists(const char *filename)
1057 {
1058  if (ast_fileexists(filename, NULL, NULL)) {
1059  return -1;
1060  }
1061  ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
1062  return 0;
1063 }
1064 
1065 /*!
1066  * \brief Announce number of users in the conference bridge to the caller
1067  *
1068  * \param conference Conference bridge to peek at
1069  * \param user Optional Caller
1070  * \param bridge_channel The bridged channel involved
1071  *
1072  * \note if caller is NULL, the announcment will be sent to all participants in the conference.
1073  * \retval 0 on success.
1074  * \retval -1 if the user hung up.
1075  */
1076 static int announce_user_count(struct confbridge_conference *conference, struct confbridge_user *user,
1078 {
1079  const char *other_in_party = conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, conference->b_profile.sounds);
1080  const char *only_one = conf_get_sound(CONF_SOUND_ONLY_ONE, conference->b_profile.sounds);
1081  const char *there_are = conf_get_sound(CONF_SOUND_THERE_ARE, conference->b_profile.sounds);
1082 
1083  if (conference->activeusers <= 1) {
1084  /* Awww we are the only person in the conference bridge OR we only have waitmarked users */
1085  return 0;
1086  } else if (conference->activeusers == 2) {
1087  if (user) {
1088  /* Eep, there is one other person */
1089  if (play_file(bridge_channel, user->chan, only_one) < 0) {
1090  return -1;
1091  }
1092  } else {
1093  play_sound_file(conference, only_one);
1094  }
1095  } else {
1096  /* Alas multiple others in here */
1097  if (user) {
1098  if (ast_stream_and_wait(user->chan,
1099  there_are,
1100  "")) {
1101  return -1;
1102  }
1103  if (ast_say_number(user->chan, conference->activeusers - 1, "", ast_channel_language(user->chan), NULL)) {
1104  return -1;
1105  }
1106  if (play_file(bridge_channel, user->chan, other_in_party) < 0) {
1107  return -1;
1108  }
1109  } else if (sound_file_exists(there_are) && sound_file_exists(other_in_party)) {
1110  play_sound_file(conference, there_are);
1111  play_sound_number(conference, conference->activeusers - 1);
1112  play_sound_file(conference, other_in_party);
1113  }
1114  }
1115  return 0;
1116 }
1117 
1118 /*!
1119  * \brief Play back an audio file to a channel
1120  *
1121  * \param user User to play audio prompt to
1122  * \param filename Prompt to play
1123  *
1124  * \retval 0 on success.
1125  * \retval -1 if the user hung up.
1126  *
1127  * \note Generally this should be called when the conference is unlocked to avoid blocking
1128  * the entire conference while the sound is played. But don't unlock the conference bridge
1129  * in the middle of a state transition.
1130  */
1131 static int play_prompt_to_user(struct confbridge_user *user, const char *filename)
1132 {
1133  return ast_stream_and_wait(user->chan, filename, "");
1134 }
1135 
1136 static void handle_video_on_join(struct confbridge_conference *conference, struct ast_channel *chan, int marked)
1137 {
1138  /* Right now, only marked users are automatically set as the single src of video.*/
1139  if (!marked) {
1140  return;
1141  }
1142 
1143  if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED)) {
1144  int set = 1;
1145  struct confbridge_user *user = NULL;
1146 
1147  ao2_lock(conference);
1148  /* see if anyone is already the video src */
1149  AST_LIST_TRAVERSE(&conference->active_list, user, list) {
1150  if (user->chan == chan) {
1151  continue;
1152  }
1153  if (ast_bridge_is_video_src(conference->bridge, user->chan)) {
1154  set = 0;
1155  break;
1156  }
1157  }
1158  ao2_unlock(conference);
1159  if (set) {
1160  ast_bridge_set_single_src_video_mode(conference->bridge, chan);
1161  }
1162  } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
1163  /* we joined and are video capable, we override anyone else that may have already been the video feed */
1164  ast_bridge_set_single_src_video_mode(conference->bridge, chan);
1165  }
1166 }
1167 
1168 static void handle_video_on_exit(struct confbridge_conference *conference, struct ast_channel *chan)
1169 {
1170  struct confbridge_user *user = NULL;
1171 
1172  /* if this isn't a video source, nothing to update */
1173  if (!ast_bridge_is_video_src(conference->bridge, chan)) {
1174  return;
1175  }
1176 
1177  ast_bridge_remove_video_src(conference->bridge, chan);
1178 
1179  /* If in follow talker mode, make sure to restore this mode on the
1180  * bridge when a source is removed. It is possible this channel was
1181  * only set temporarily as a video source by an AMI or DTMF action. */
1182  if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
1184  }
1185 
1186  /* if the video_mode isn't set to automatically pick the video source, do nothing on exit. */
1187  if (!ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) &&
1188  !ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
1189  return;
1190  }
1191 
1192  /* Make the next available marked user the video src. */
1193  ao2_lock(conference);
1194  AST_LIST_TRAVERSE(&conference->active_list, user, list) {
1195  if (user->chan == chan) {
1196  continue;
1197  }
1198  if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
1199  ast_bridge_set_single_src_video_mode(conference->bridge, user->chan);
1200  break;
1201  }
1202  }
1203  ao2_unlock(conference);
1204 }
1205 
1207 {
1208  struct confbridge_conference *conference;
1209  ast_mutex_t lock;
1210  ast_cond_t cond;
1211  int hungup;
1212 };
1213 
1214 /*!
1215  * \brief Hang up the announcer channel
1216  *
1217  * This hangs up the announcer channel in the conference. This
1218  * runs in the playback queue taskprocessor since we do not want
1219  * to hang up the channel while it's trying to play an announcement.
1220  *
1221  * This task is performed synchronously, so there is no need to
1222  * perform any cleanup on the passed-in data.
1223  *
1224  * \param data A hangup_data structure
1225  * \return 0
1226  */
1227 static int hangup_playback(void *data)
1228 {
1229  struct hangup_data *hangup = data;
1230 
1231  ast_autoservice_stop(hangup->conference->playback_chan);
1232 
1233  ast_hangup(hangup->conference->playback_chan);
1234  hangup->conference->playback_chan = NULL;
1235 
1236  ast_mutex_lock(&hangup->lock);
1237  hangup->hungup = 1;
1238  ast_cond_signal(&hangup->cond);
1239  ast_mutex_unlock(&hangup->lock);
1240 
1241  return 0;
1242 }
1243 
1244 static void hangup_data_init(struct hangup_data *hangup, struct confbridge_conference *conference)
1245 {
1246  ast_mutex_init(&hangup->lock);
1247  ast_cond_init(&hangup->cond, NULL);
1248 
1249  hangup->conference = conference;
1250  hangup->hungup = 0;
1251 }
1252 
1253 static void hangup_data_destroy(struct hangup_data *hangup)
1254 {
1255  ast_mutex_destroy(&hangup->lock);
1256  ast_cond_destroy(&hangup->cond);
1257 }
1258 
1259 /*!
1260  * \brief Destroy a conference bridge
1261  *
1262  * \param obj The conference bridge object
1263  */
1264 static void destroy_conference_bridge(void *obj)
1265 {
1266  struct confbridge_conference *conference = obj;
1267 
1268  ast_debug(1, "Destroying conference bridge '%s'\n", conference->name);
1269 
1270  if (conference->playback_chan) {
1271  if (conference->playback_queue) {
1272  struct hangup_data hangup;
1273  hangup_data_init(&hangup, conference);
1274 
1275  if (!ast_taskprocessor_push(conference->playback_queue, hangup_playback, &hangup)) {
1276  ast_mutex_lock(&hangup.lock);
1277  while (!hangup.hungup) {
1278  ast_cond_wait(&hangup.cond, &hangup.lock);
1279  }
1280  ast_mutex_unlock(&hangup.lock);
1281  }
1282 
1283  hangup_data_destroy(&hangup);
1284  } else {
1285  /* Playback queue is not yet allocated. Just hang up the channel straight */
1286  ast_hangup(conference->playback_chan);
1287  conference->playback_chan = NULL;
1288  }
1289  }
1290 
1291  /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
1292  if (conference->bridge) {
1293  ast_bridge_destroy(conference->bridge, 0);
1294  conference->bridge = NULL;
1295  }
1296 
1297  ast_channel_cleanup(conference->record_chan);
1298  ast_free(conference->orig_rec_file);
1299  ast_free(conference->record_filename);
1300 
1301  conf_bridge_profile_destroy(&conference->b_profile);
1303 }
1304 
1305 /*! \brief Call the proper join event handler for the user for the conference bridge's current state
1306  * \internal
1307  * \param user The conference bridge user that is joining
1308  * \retval 0 success
1309  * \retval -1 failure
1310  */
1311 static int handle_conf_user_join(struct confbridge_user *user)
1312 {
1313  conference_event_fn handler;
1314  if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
1315  handler = user->conference->state->join_marked;
1316  } else if (ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)) {
1317  handler = user->conference->state->join_waitmarked;
1318  } else {
1319  handler = user->conference->state->join_unmarked;
1320  }
1321 
1322  ast_assert(handler != NULL);
1323 
1324  if (!handler) {
1325  conf_invalid_event_fn(user);
1326  return -1;
1327  }
1328 
1329  handler(user);
1330 
1331  return 0;
1332 }
1333 
1334 /*! \brief Call the proper leave event handler for the user for the conference bridge's current state
1335  * \internal
1336  * \param user The conference bridge user that is leaving
1337  * \retval 0 success
1338  * \retval -1 failure
1339  */
1340 static int handle_conf_user_leave(struct confbridge_user *user)
1341 {
1342  conference_event_fn handler;
1343  if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
1344  handler = user->conference->state->leave_marked;
1345  } else if (ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)) {
1346  handler = user->conference->state->leave_waitmarked;
1347  } else {
1348  handler = user->conference->state->leave_unmarked;
1349  }
1350 
1351  ast_assert(handler != NULL);
1352 
1353  if (!handler) {
1354  /* This should never happen. If it does, though, it is bad. The user will not have been removed
1355  * from the appropriate list, so counts will be off and stuff. The conference won't be torn down, etc.
1356  * Shouldn't happen, though. */
1357  conf_invalid_event_fn(user);
1358  return -1;
1359  }
1360 
1361  handler(user);
1362 
1363  return 0;
1364 }
1365 
1367 {
1368  int mute_user;
1369  int mute_system;
1370  int mute_effective;
1371 
1372  /* User level mute request. */
1373  mute_user = user->muted;
1374 
1375  /* System level mute request. */
1376  mute_system = user->playing_moh
1377  /*
1378  * Do not allow waitmarked users to talk to anyone unless there
1379  * is a marked user present.
1380  */
1381  || (!user->conference->markedusers
1382  && ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED));
1383 
1384  mute_effective = mute_user || mute_system;
1385 
1386  ast_debug(1, "User %s is %s: user:%d system:%d.\n",
1387  ast_channel_name(user->chan), mute_effective ? "muted" : "unmuted",
1388  mute_user, mute_system);
1389  user->features.mute = mute_effective;
1390  ast_test_suite_event_notify("CONF_MUTE_UPDATE",
1391  "Mode: %s\r\n"
1392  "Conference: %s\r\n"
1393  "Channel: %s",
1394  mute_effective ? "muted" : "unmuted",
1395  user->conference->b_profile.name,
1396  ast_channel_name(user->chan));
1397 }
1398 
1399 /*!
1400  * \internal
1401  * \brief Mute/unmute a single user.
1402  */
1403 static void generic_mute_unmute_user(struct confbridge_conference *conference, struct confbridge_user *user, int mute)
1404 {
1405  /* Set user level mute request. */
1406  user->muted = mute ? 1 : 0;
1407 
1408  conf_update_user_mute(user);
1409  ast_test_suite_event_notify("CONF_MUTE",
1410  "Message: participant %s %s\r\n"
1411  "Conference: %s\r\n"
1412  "Channel: %s",
1413  ast_channel_name(user->chan),
1414  mute ? "muted" : "unmuted",
1415  conference->b_profile.name,
1416  ast_channel_name(user->chan));
1417  if (mute) {
1418  send_mute_event(user, conference);
1419  } else {
1420  send_unmute_event(user, conference);
1421  }
1422 }
1423 
1424 void conf_moh_stop(struct confbridge_user *user)
1425 {
1426  user->playing_moh = 0;
1427  if (!user->suspended_moh) {
1428  int in_bridge;
1429 
1430  /*
1431  * Locking the ast_bridge here is the only way to hold off the
1432  * call to ast_bridge_join() in confbridge_exec() from
1433  * interfering with the bridge and MOH operations here.
1434  */
1436 
1437  /*
1438  * Temporarily suspend the user from the bridge so we have
1439  * control to stop MOH if needed.
1440  */
1441  in_bridge = !ast_bridge_suspend(user->conference->bridge, user->chan);
1442  ast_moh_stop(user->chan);
1443  if (in_bridge) {
1444  ast_bridge_unsuspend(user->conference->bridge, user->chan);
1445  }
1446 
1448  }
1449 }
1450 
1452 {
1453  user->playing_moh = 1;
1454  if (!user->suspended_moh) {
1455  int in_bridge;
1456 
1457  /*
1458  * Locking the ast_bridge here is the only way to hold off the
1459  * call to ast_bridge_join() in confbridge_exec() from
1460  * interfering with the bridge and MOH operations here.
1461  */
1463 
1464  /*
1465  * Temporarily suspend the user from the bridge so we have
1466  * control to start MOH if needed.
1467  */
1468  in_bridge = !ast_bridge_suspend(user->conference->bridge, user->chan);
1469  ast_moh_start(user->chan, user->u_profile.moh_class, NULL);
1470  if (in_bridge) {
1471  ast_bridge_unsuspend(user->conference->bridge, user->chan);
1472  }
1473 
1475  }
1476 }
1477 
1478 /*!
1479  * \internal
1480  * \brief Unsuspend MOH for the conference user.
1481  *
1482  * \param user Conference user to unsuspend MOH on.
1483  */
1484 static void conf_moh_unsuspend(struct confbridge_user *user)
1485 {
1486  ao2_lock(user->conference);
1487  if (--user->suspended_moh == 0 && user->playing_moh) {
1488  ast_moh_start(user->chan, user->u_profile.moh_class, NULL);
1489  }
1490  ao2_unlock(user->conference);
1491 }
1492 
1493 /*!
1494  * \internal
1495  * \brief Suspend MOH for the conference user.
1496  *
1497  * \param user Conference user to suspend MOH on.
1498  */
1499 static void conf_moh_suspend(struct confbridge_user *user)
1500 {
1501  ao2_lock(user->conference);
1502  if (user->suspended_moh++ == 0 && user->playing_moh) {
1503  ast_moh_stop(user->chan);
1504  }
1505  ao2_unlock(user->conference);
1506 }
1507 
1509 {
1510  /* If we have not been quieted play back that they are waiting for the leader */
1511  if (!ast_test_flag(&user->u_profile, USER_OPT_QUIET) && play_prompt_to_user(user,
1512  conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, user->conference->b_profile.sounds))) {
1513  /* user hungup while the sound was playing */
1514  return -1;
1515  }
1516  return 0;
1517 }
1518 
1520 {
1521  /* If audio prompts have not been quieted or this prompt quieted play it on out */
1522  if (!ast_test_flag(&user->u_profile, USER_OPT_QUIET | USER_OPT_NOONLYPERSON)) {
1523  if (play_prompt_to_user(user,
1524  conf_get_sound(CONF_SOUND_ONLY_PERSON, user->conference->b_profile.sounds))) {
1525  /* user hungup while the sound was playing */
1526  return -1;
1527  }
1528  }
1529  return 0;
1530 }
1531 
1532 int conf_add_post_join_action(struct confbridge_user *user, int (*func)(struct confbridge_user *user))
1533 {
1534  struct post_join_action *action;
1535  if (!(action = ast_calloc(1, sizeof(*action)))) {
1536  return -1;
1537  }
1538  action->func = func;
1539  AST_LIST_INSERT_TAIL(&user->post_join_list, action, list);
1540  return 0;
1541 }
1542 
1543 
1545 {
1546  ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "confbridge:%s", conference->name);
1547 }
1548 
1550 {
1551  /* If we are the second participant we may need to stop music on hold on the first */
1552  struct confbridge_user *first_user = AST_LIST_FIRST(&conference->active_list);
1553 
1554  if (ast_test_flag(&first_user->u_profile, USER_OPT_MUSICONHOLD)) {
1555  conf_moh_stop(first_user);
1556  }
1557  conf_update_user_mute(first_user);
1558 }
1559 
1560 void conf_ended(struct confbridge_conference *conference)
1561 {
1562  struct pbx_find_info q = { .stacklen = 0 };
1563 
1564  /* Called with a reference to conference */
1565  ao2_unlink(conference_bridges, conference);
1566  send_conf_end_event(conference);
1567  if (!ast_strlen_zero(conference->b_profile.regcontext) &&
1568  pbx_find_extension(NULL, NULL, &q, conference->b_profile.regcontext,
1569  conference->name, 1, NULL, "", E_MATCH)) {
1570  ast_context_remove_extension(conference->b_profile.regcontext,
1571  conference->name, 1, NULL);
1572  }
1573  ao2_lock(conference);
1574  conf_stop_record(conference);
1575  ao2_unlock(conference);
1576 }
1577 
1578 /*!
1579  * \internal
1580  * \brief Allocate playback channel for a conference.
1581  * \pre expects conference to be locked before calling this function
1582  */
1583 static int alloc_playback_chan(struct confbridge_conference *conference)
1584 {
1585  struct ast_format_cap *cap;
1586  char taskprocessor_name[AST_TASKPROCESSOR_MAX_NAME + 1];
1587 
1589  if (!cap) {
1590  return -1;
1591  }
1593  conference->playback_chan = ast_request("CBAnn", cap, NULL, NULL,
1594  conference->name, NULL);
1595  ao2_ref(cap, -1);
1596  if (!conference->playback_chan) {
1597  return -1;
1598  }
1599 
1600  /* To make sure playback_chan has the same language as the bridge */
1601  ast_channel_lock(conference->playback_chan);
1602  ast_channel_language_set(conference->playback_chan, conference->b_profile.language);
1603  ast_channel_unlock(conference->playback_chan);
1604 
1605  ast_debug(1, "Created announcer channel '%s' to conference bridge '%s'\n",
1606  ast_channel_name(conference->playback_chan), conference->name);
1607 
1608  ast_taskprocessor_build_name(taskprocessor_name, sizeof(taskprocessor_name),
1609  "Confbridge/%s", conference->name);
1610  conference->playback_queue = ast_taskprocessor_get(taskprocessor_name, TPS_REF_DEFAULT);
1611  if (!conference->playback_queue) {
1612  ast_hangup(conference->playback_chan);
1613  conference->playback_chan = NULL;
1614  return -1;
1615  }
1616  return 0;
1617 }
1618 
1619 /*!
1620  * \brief Push the announcer channel into the bridge
1621  *
1622  * \param conference Conference bridge to push the announcer to
1623  * \retval 0 Success
1624  * \retval -1 Failed to push the channel to the bridge
1625  */
1626 static int push_announcer(struct confbridge_conference *conference)
1627 {
1628  if (conf_announce_channel_push(conference->playback_chan)) {
1629  ast_hangup(conference->playback_chan);
1630  conference->playback_chan = NULL;
1631  return -1;
1632  }
1633 
1634  ast_autoservice_start(conference->playback_chan);
1635  return 0;
1636 }
1637 
1638 static void confbridge_unlock_and_unref(void *obj)
1639 {
1640  struct confbridge_conference *conference = obj;
1641 
1642  if (!obj) {
1643  return;
1644  }
1645  ao2_unlock(conference);
1646  ao2_ref(conference, -1);
1647 }
1648 
1650 {
1651  struct ast_channel_snapshot *old_snapshot;
1652  struct ast_channel_snapshot *new_snapshot;
1653  char *confbr_name = NULL;
1654  char *comma;
1655  RAII_VAR(struct confbridge_conference *, conference, NULL, confbridge_unlock_and_unref);
1656  struct confbridge_user *user = NULL;
1657  int found_user = 0;
1658  struct ast_json *json_object;
1659 
1660  if (msg->to_transferee.channel_snapshot
1661  && strcmp(msg->to_transferee.channel_snapshot->dialplan->appl, "ConfBridge") == 0
1662  && msg->target) {
1663  /* We're transferring a bridge to an extension */
1664  old_snapshot = msg->to_transferee.channel_snapshot;
1665  new_snapshot = msg->target;
1666  } else if (msg->to_transfer_target.channel_snapshot
1667  && strcmp(msg->to_transfer_target.channel_snapshot->dialplan->appl, "ConfBridge") == 0
1668  && msg->transferee) {
1669  /* We're transferring a call to a bridge */
1670  old_snapshot = msg->to_transfer_target.channel_snapshot;
1671  new_snapshot = msg->transferee;
1672  } else {
1673  ast_log(LOG_ERROR, "Could not determine proper channels\n");
1674  return;
1675  }
1676 
1677  /*
1678  * old_snapshot->data should have the original parameters passed to
1679  * the ConfBridge app:
1680  * conference[,bridge_profile[,user_profile[,menu]]]
1681  * We'll use "conference" to look up the bridge.
1682  *
1683  * We _could_ use old_snapshot->bridgeid to get the bridge but
1684  * that would involve locking the conference_bridges container
1685  * and iterating over it looking for a matching bridge.
1686  */
1687  if (ast_strlen_zero(old_snapshot->dialplan->data)) {
1688  ast_log(LOG_ERROR, "Channel '%s' didn't have app data set\n", old_snapshot->base->name);
1689  return;
1690  }
1691  confbr_name = ast_strdupa(old_snapshot->dialplan->data);
1692  comma = strchr(confbr_name, ',');
1693  if (comma) {
1694  *comma = '\0';
1695  }
1696 
1697  ast_debug(1, "Confbr: %s Leaving: %s Joining: %s\n", confbr_name, old_snapshot->base->name, new_snapshot->base->name);
1698 
1699  conference = ao2_find(conference_bridges, confbr_name, OBJ_SEARCH_KEY);
1700  if (!conference) {
1701  ast_log(LOG_ERROR, "Conference bridge '%s' not found\n", confbr_name);
1702  return;
1703  }
1704  ao2_lock(conference);
1705 
1706  /*
1707  * We need to grab the user profile for the departing user in order to
1708  * properly format the join/leave messages.
1709  */
1710  AST_LIST_TRAVERSE(&conference->active_list, user, list) {
1711  if (strcasecmp(ast_channel_name(user->chan), old_snapshot->base->name) == 0) {
1712  found_user = 1;
1713  break;
1714  }
1715  }
1716 
1717  /*
1718  * If we didn't find the user in the active list, try the waiting list.
1719  */
1720  if (!found_user && conference->waitingusers) {
1721  AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
1722  if (strcasecmp(ast_channel_name(user->chan), old_snapshot->base->name) == 0) {
1723  found_user = 1;
1724  break;
1725  }
1726  }
1727  }
1728 
1729  if (!found_user) {
1730  ast_log(LOG_ERROR, "Unable to find user profile for channel '%s' in bridge '%s'\n",
1731  old_snapshot->base->name, confbr_name);
1732  return;
1733  }
1734 
1735  /*
1736  * We're going to use the existing user profile to create the messages.
1737  */
1738  json_object = ast_json_pack("{s: b}",
1739  "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN)
1740  );
1741  if (!json_object) {
1742  return;
1743  }
1744 
1745  send_conf_stasis_snapshots(conference, old_snapshot, confbridge_leave_type(), json_object);
1746  ast_json_unref(json_object);
1747 
1748  json_object = ast_json_pack("{s: b, s: b}",
1749  "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN),
1750  "muted", user->muted);
1751  if (!json_object) {
1752  return;
1753  }
1754  send_conf_stasis_snapshots(conference, new_snapshot, confbridge_join_type(), json_object);
1755  ast_json_unref(json_object);
1756 }
1757 
1758 /*!
1759  * \brief Join a conference bridge
1760  *
1761  * \param conference_name The conference name
1762  * \param user Conference bridge user structure
1763  *
1764  * \return A pointer to the conference bridge struct, or NULL if the conference room wasn't found.
1765  */
1766 static struct confbridge_conference *join_conference_bridge(const char *conference_name, struct confbridge_user *user)
1767 {
1768  struct confbridge_conference *conference;
1769  struct post_join_action *action;
1770  int max_members_reached = 0;
1771 
1772  /* We explicitly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same time */
1773  ao2_lock(conference_bridges);
1774 
1775  ast_debug(1, "Trying to find conference bridge '%s'\n", conference_name);
1776 
1777  /* Attempt to find an existing conference bridge */
1778  conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
1779  if (conference && conference->b_profile.max_members) {
1780  max_members_reached = conference->b_profile.max_members > conference->activeusers ? 0 : 1;
1781  }
1782 
1783  /* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */
1784  if (conference && (max_members_reached || conference->locked) && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
1785  ao2_unlock(conference_bridges);
1786  ast_debug(1, "Conference '%s' is locked and caller is not an admin\n", conference_name);
1787  ast_stream_and_wait(user->chan,
1788  conf_get_sound(CONF_SOUND_LOCKED, conference->b_profile.sounds),
1789  "");
1790  ao2_ref(conference, -1);
1791  return NULL;
1792  }
1793 
1794  /* If no conference bridge was found see if we can create one */
1795  if (!conference) {
1796  /* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */
1797  if (!(conference = ao2_alloc(sizeof(*conference), destroy_conference_bridge))) {
1798  ao2_unlock(conference_bridges);
1799  ast_log(LOG_ERROR, "Conference '%s' could not be created.\n", conference_name);
1800  return NULL;
1801  }
1802 
1803  /* Setup for the record channel */
1805  if (!conference->record_filename) {
1806  ao2_ref(conference, -1);
1807  ao2_unlock(conference_bridges);
1808  return NULL;
1809  }
1810 
1811  /* Setup conference bridge parameters */
1812  ast_copy_string(conference->name, conference_name, sizeof(conference->name));
1813  conf_bridge_profile_copy(&conference->b_profile, &user->b_profile);
1814 
1815  /* Create an actual bridge that will do the audio mixing */
1818  app, conference_name, NULL);
1819  if (!conference->bridge) {
1820  ao2_ref(conference, -1);
1821  ao2_unlock(conference_bridges);
1822  ast_log(LOG_ERROR, "Conference '%s' mixing bridge could not be created.\n", conference_name);
1823  return NULL;
1824  }
1825 
1826  /* Set the internal sample rate on the bridge from the bridge profile */
1828  /* Set the maximum sample rate on the bridge from the bridge profile */
1830  /* Set the internal mixing interval on the bridge from the bridge profile */
1831  ast_bridge_set_mixing_interval(conference->bridge, conference->b_profile.mix_interval);
1832  ast_bridge_set_binaural_active(conference->bridge, ast_test_flag(&conference->b_profile, BRIDGE_OPT_BINAURAL_ACTIVE));
1833 
1834  if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
1836  } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_SFU)) {
1839  if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE)) {
1841  } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_LOWEST)) {
1843  } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST)) {
1845  } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE_ALL)) {
1847  } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_LOWEST_ALL)) {
1849  } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST_ALL)) {
1851  } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_FORCE)) {
1854  }
1855  }
1856 
1857  /* Always set the minimum interval between video updates, to avoid infinite video updates. */
1859 
1860  if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_ENABLE_EVENTS)) {
1861  ast_bridge_set_send_sdp_label(conference->bridge, 1);
1862  }
1863 
1864  /* Link it into the conference bridges container */
1865  if (!ao2_link(conference_bridges, conference)) {
1866  ao2_ref(conference, -1);
1867  ao2_unlock(conference_bridges);
1868  ast_log(LOG_ERROR,
1869  "Conference '%s' could not be added to the conferences list.\n", conference_name);
1870  return NULL;
1871  }
1872 
1873  /* Set the initial state to EMPTY */
1874  conference->state = CONF_STATE_EMPTY;
1875 
1876  if (alloc_playback_chan(conference)) {
1877  ao2_unlink(conference_bridges, conference);
1878  ao2_ref(conference, -1);
1879  ao2_unlock(conference_bridges);
1880  ast_log(LOG_ERROR, "Could not allocate announcer channel for conference '%s'\n", conference_name);
1881  return NULL;
1882  }
1883 
1884  if (push_announcer(conference)) {
1885  ao2_unlink(conference_bridges, conference);
1886  ao2_ref(conference, -1);
1887  ao2_unlock(conference_bridges);
1888  ast_log(LOG_ERROR, "Could not add announcer channel for conference '%s' bridge\n", conference_name);
1889  return NULL;
1890  }
1891 
1892  if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_CONFERENCE)) {
1893  ao2_lock(conference);
1894  conf_start_record(conference);
1895  ao2_unlock(conference);
1896  }
1897 
1898  send_conf_start_event(conference);
1899 
1900  if (!ast_strlen_zero(conference->b_profile.regcontext)) {
1901  if (!ast_exists_extension(NULL, conference->b_profile.regcontext, conference->name, 1, NULL)) {
1902  ast_add_extension(conference->b_profile.regcontext, 1, conference->name, 1, NULL, NULL, "Noop", NULL, NULL, "ConfBridge");
1903  }
1904  }
1905 
1906  ast_debug(1, "Created conference '%s' and linked to container.\n", conference_name);
1907  }
1908 
1909  ao2_unlock(conference_bridges);
1910 
1911  /* Setup conference bridge user parameters */
1912  user->conference = conference;
1913 
1914  ao2_lock(conference);
1915 
1916  /* Determine if the new user should join the conference muted. */
1917  if (ast_test_flag(&user->u_profile, USER_OPT_STARTMUTED)
1918  || (!ast_test_flag(&user->u_profile, USER_OPT_ADMIN) && conference->muted)) {
1919  /* Set user level mute request. */
1920  user->muted = 1;
1921  }
1922 
1923  /*
1924  * Suspend any MOH until the user actually joins the bridge of
1925  * the conference. This way any pre-join file playback does not
1926  * need to worry about MOH.
1927  */
1928  user->suspended_moh = 1;
1929 
1930  if (handle_conf_user_join(user)) {
1931  /* Invalid event, nothing was done, so we don't want to process a leave. */
1932  ao2_unlock(conference);
1933  ao2_ref(conference, -1);
1934  user->conference = NULL;
1935  return NULL;
1936  }
1937 
1938  if (ast_check_hangup(user->chan)) {
1939  ao2_unlock(conference);
1940  leave_conference(user);
1941  return NULL;
1942  }
1943 
1944  ao2_unlock(conference);
1945 
1946  /* If an announcement is to be played play it */
1947  if (!ast_strlen_zero(user->u_profile.announcement)) {
1948  if (play_prompt_to_user(user,
1949  user->u_profile.announcement)) {
1950  leave_conference(user);
1951  return NULL;
1952  }
1953  }
1954 
1955  /* Announce number of users if need be */
1956  if (ast_test_flag(&user->u_profile, USER_OPT_ANNOUNCEUSERCOUNT)) {
1957  if (announce_user_count(conference, user, NULL)) {
1958  leave_conference(user);
1959  return NULL;
1960  }
1961  }
1962 
1963  if (ast_test_flag(&user->u_profile, USER_OPT_ANNOUNCEUSERCOUNTALL) &&
1964  (conference->activeusers > user->u_profile.announce_user_count_all_after)) {
1965  int user_count_res;
1966 
1967  /*
1968  * We have to autoservice the new user because he has not quite
1969  * joined the conference yet.
1970  */
1971  ast_autoservice_start(user->chan);
1972  user_count_res = announce_user_count(conference, NULL, NULL);
1973  ast_autoservice_stop(user->chan);
1974  if (user_count_res) {
1975  leave_conference(user);
1976  return NULL;
1977  }
1978  }
1979 
1980  /* Handle post-join actions */
1981  while ((action = AST_LIST_REMOVE_HEAD(&user->post_join_list, list))) {
1982  action->func(user);
1983  ast_free(action);
1984  }
1985 
1986  return conference;
1987 }
1988 
1989 /*!
1990  * \brief Leave a conference
1991  *
1992  * \param user The conference user
1993  */
1994 static void leave_conference(struct confbridge_user *user)
1995 {
1996  struct post_join_action *action;
1997 
1998  ao2_lock(user->conference);
1999  handle_conf_user_leave(user);
2000  ao2_unlock(user->conference);
2001 
2002  /* Discard any post-join actions */
2003  while ((action = AST_LIST_REMOVE_HEAD(&user->post_join_list, list))) {
2004  ast_free(action);
2005  }
2006 
2007  /* Done mucking with the conference, huzzah */
2008  ao2_ref(user->conference, -1);
2009  user->conference = NULL;
2010 }
2011 
2012 static void playback_common(struct confbridge_conference *conference, const char *filename, int say_number)
2013 {
2014  /* Don't try to play if the playback channel has been hung up */
2015  if (!conference->playback_chan) {
2016  return;
2017  }
2018 
2019  ast_autoservice_stop(conference->playback_chan);
2020 
2021  /* The channel is all under our control, in goes the prompt */
2022  if (!ast_strlen_zero(filename)) {
2023  ast_stream_and_wait(conference->playback_chan, filename, "");
2024  } else if (say_number >= 0) {
2025  ast_say_number(conference->playback_chan, say_number, "",
2026  ast_channel_language(conference->playback_chan), NULL);
2027  }
2028 
2029  ast_autoservice_start(conference->playback_chan);
2030 }
2031 
2033  struct confbridge_conference *conference;
2034  const char *filename;
2035  int say_number;
2036  int playback_finished;
2037  ast_mutex_t lock;
2038  ast_cond_t cond;
2039 };
2040 
2041 /*!
2042  * \brief Play an announcement into a confbridge
2043  *
2044  * This runs in the playback queue taskprocessor. This ensures that
2045  * all playbacks are handled in sequence and do not play over top one
2046  * another.
2047  *
2048  * This task runs synchronously so there is no need for performing any
2049  * sort of cleanup on the input parameter.
2050  *
2051  * \param data A playback_task_data
2052  * \return 0
2053  */
2054 static int playback_task(void *data)
2055 {
2056  struct playback_task_data *ptd = data;
2057 
2058  playback_common(ptd->conference, ptd->filename, ptd->say_number);
2059 
2060  ast_mutex_lock(&ptd->lock);
2061  ptd->playback_finished = 1;
2062  ast_cond_signal(&ptd->cond);
2063  ast_mutex_unlock(&ptd->lock);
2064 
2065  return 0;
2066 }
2067 
2068 static void playback_task_data_init(struct playback_task_data *ptd, struct confbridge_conference *conference,
2069  const char *filename, int say_number)
2070 {
2071  ast_mutex_init(&ptd->lock);
2072  ast_cond_init(&ptd->cond, NULL);
2073 
2074  ptd->filename = filename;
2075  ptd->say_number = say_number;
2076  ptd->conference = conference;
2077  ptd->playback_finished = 0;
2078 }
2079 
2080 static void playback_task_data_destroy(struct playback_task_data *ptd)
2081 {
2082  ast_mutex_destroy(&ptd->lock);
2083  ast_cond_destroy(&ptd->cond);
2084 }
2085 
2086 static int play_sound_helper(struct confbridge_conference *conference, const char *filename, int say_number)
2087 {
2088  struct playback_task_data ptd;
2089 
2090  /* Do not waste resources trying to play files that do not exist */
2091  if (ast_strlen_zero(filename)) {
2092  if (say_number < 0) {
2093  return 0;
2094  }
2095  } else if (!sound_file_exists(filename)) {
2096  return 0;
2097  }
2098 
2099  playback_task_data_init(&ptd, conference, filename, say_number);
2100  if (ast_taskprocessor_push(conference->playback_queue, playback_task, &ptd)) {
2101  if (!ast_strlen_zero(filename)) {
2102  ast_log(LOG_WARNING, "Unable to play file '%s' to conference %s\n",
2103  filename, conference->name);
2104  } else {
2105  ast_log(LOG_WARNING, "Unable to say number '%d' to conference %s\n",
2106  say_number, conference->name);
2107  }
2108  playback_task_data_destroy(&ptd);
2109  return -1;
2110  }
2111 
2112  /* Wait for the playback to complete */
2113  ast_mutex_lock(&ptd.lock);
2114  while (!ptd.playback_finished) {
2115  ast_cond_wait(&ptd.cond, &ptd.lock);
2116  }
2117  ast_mutex_unlock(&ptd.lock);
2118 
2119  playback_task_data_destroy(&ptd);
2120 
2121  return 0;
2122 }
2123 
2124 int play_sound_file(struct confbridge_conference *conference, const char *filename)
2125 {
2126  return play_sound_helper(conference, filename, -1);
2127 }
2128 
2130  struct confbridge_conference *conference;
2131  int say_number;
2132  struct ast_channel *initiator;
2133  char filename[0];
2134 };
2135 
2137  ast_mutex_t lock;
2138  ast_cond_t cond;
2139  int wait;
2140 };
2141 
2142 static void async_datastore_data_destroy(void *data)
2143 {
2144  struct async_datastore_data *add = data;
2145 
2146  ast_mutex_destroy(&add->lock);
2147  ast_cond_destroy(&add->cond);
2148 
2149  ast_free(add);
2150 }
2151 
2152 /*!
2153  * \brief Datastore used for timing of async announcement playback
2154  *
2155  * Announcements that are played to the entire conference can be played
2156  * asynchronously (i.e. The channel that queues the playback does not wait
2157  * for the playback to complete before continuing)
2158  *
2159  * The thing about async announcements is that the channel that queues the
2160  * announcement is either not in the bridge or is in some other way "occupied"
2161  * at the time the announcement is queued. Because of that, the initiator of
2162  * the announcement may enter after the announcement has already started,
2163  * resulting in the sound being "clipped".
2164  *
2165  * This datastore makes it so that the channel that queues the async announcement
2166  * can say "I'm ready now". This way the announcement does not start until the
2167  * initiator of the announcement is ready to hear the sound.
2168  */
2170  .type = "Confbridge async playback",
2171  .destroy = async_datastore_data_destroy,
2172 };
2173 
2174 static struct async_datastore_data *async_datastore_data_alloc(void)
2175 {
2176  struct async_datastore_data *add;
2177 
2178  add = ast_malloc(sizeof(*add));
2179  if (!add) {
2180  return NULL;
2181  }
2182 
2183  ast_mutex_init(&add->lock);
2184  ast_cond_init(&add->cond, NULL);
2185  add->wait = 1;
2186 
2187  return add;
2188 }
2189 
2190 /*!
2191  * \brief Prepare the async playback datastore
2192  *
2193  * This is done prior to queuing an async announcement. If the
2194  * datastore has not yet been created, it is allocated and initialized.
2195  * If it already exists, we set it to be in "waiting" mode.
2196  *
2197  * \param initiator The channel that is queuing the async playback
2198  * \retval 0 Success
2199  * \retval -1 Failure :(
2200  */
2201 static int setup_async_playback_datastore(struct ast_channel *initiator)
2202 {
2203  struct ast_datastore *async_datastore;
2204 
2205  async_datastore = ast_channel_datastore_find(initiator, &async_datastore_info, NULL);
2206  if (async_datastore) {
2207  struct async_datastore_data *add;
2208 
2209  add = async_datastore->data;
2210  add->wait = 1;
2211 
2212  return 0;
2213  }
2214 
2215  async_datastore = ast_datastore_alloc(&async_datastore_info, NULL);
2216  if (!async_datastore) {
2217  return -1;
2218  }
2219 
2220  async_datastore->data = async_datastore_data_alloc();
2221  if (!async_datastore->data) {
2222  ast_datastore_free(async_datastore);
2223  return -1;
2224  }
2225 
2226  ast_channel_datastore_add(initiator, async_datastore);
2227  return 0;
2228 }
2229 
2230 static struct async_playback_task_data *async_playback_task_data_alloc(
2231  struct confbridge_conference *conference, const char *filename, int say_number,
2232  struct ast_channel *initiator)
2233 {
2234  struct async_playback_task_data *aptd;
2235 
2236  aptd = ast_malloc(sizeof(*aptd) + strlen(filename) + 1);
2237  if (!aptd) {
2238  return NULL;
2239  }
2240 
2241  /* Safe */
2242  strcpy(aptd->filename, filename);
2243  aptd->say_number = say_number;
2244 
2245  /* You may think that we need to bump the conference refcount since we are pushing
2246  * this task to the taskprocessor.
2247  *
2248  * In this case, that actually causes a problem. The destructor for the conference
2249  * pushes a hangup task into the taskprocessor and waits for it to complete before
2250  * continuing. If the destructor gets called from a taskprocessor task, we're
2251  * deadlocked.
2252  *
2253  * So is there a risk of the conference being freed out from under us? No. Since
2254  * the destructor pushes a task into the taskprocessor and waits for it to complete,
2255  * the destructor cannot free the conference out from under us. No further tasks
2256  * can be queued onto the taskprocessor after the hangup since no channels are referencing
2257  * the conference at that point any more.
2258  */
2259  aptd->conference = conference;
2260 
2261  aptd->initiator = initiator;
2262  if (initiator) {
2263  ast_channel_ref(initiator);
2264  ast_channel_lock(aptd->initiator);
2265  /* We don't really care if this fails. If the datastore fails to get set up
2266  * we'll still play the announcement. It's possible that the sound will be
2267  * clipped for the initiator, but that's not the end of the world.
2268  */
2269  setup_async_playback_datastore(aptd->initiator);
2270  ast_channel_unlock(aptd->initiator);
2271  }
2272 
2273  return aptd;
2274 }
2275 
2276 static void async_playback_task_data_destroy(struct async_playback_task_data *aptd)
2277 {
2278  ast_channel_cleanup(aptd->initiator);
2279  ast_free(aptd);
2280 }
2281 
2282 /*!
2283  * \brief Wait for the initiator of an async playback to be ready
2284  *
2285  * See the description on the async_datastore_info structure for more
2286  * information about what this is about.
2287  *
2288  * \param initiator The channel that queued the async announcement
2289  */
2290 static void wait_for_initiator(struct ast_channel *initiator)
2291 {
2292  struct ast_datastore *async_datastore;
2293  struct async_datastore_data *add;
2294 
2295  ast_channel_lock(initiator);
2296  async_datastore = ast_channel_datastore_find(initiator, &async_datastore_info, NULL);
2297  ast_channel_unlock(initiator);
2298 
2299  if (!async_datastore) {
2300  return;
2301  }
2302 
2303  add = async_datastore->data;
2304 
2305  ast_mutex_lock(&add->lock);
2306  while (add->wait) {
2307  ast_cond_wait(&add->cond, &add->lock);
2308  }
2309  ast_mutex_unlock(&add->lock);
2310 }
2311 
2312 /*!
2313  * \brief Play an announcement into a confbridge asynchronously
2314  *
2315  * This runs in the playback queue taskprocessor. This ensures that
2316  * all playbacks are handled in sequence and do not play over top one
2317  * another.
2318  *
2319  * \param data An async_playback_task_data
2320  * \return 0
2321  */
2322 static int async_playback_task(void *data)
2323 {
2324  struct async_playback_task_data *aptd = data;
2325 
2326  /* Wait for the initiator to get back in the bridge or be hung up */
2327  if (aptd->initiator) {
2328  wait_for_initiator(aptd->initiator);
2329  }
2330 
2331  playback_common(aptd->conference, aptd->filename, aptd->say_number);
2332 
2333  async_playback_task_data_destroy(aptd);
2334  return 0;
2335 }
2336 
2337 static int async_play_sound_helper(struct confbridge_conference *conference,
2338  const char *filename, int say_number, struct ast_channel *initiator)
2339 {
2340  struct async_playback_task_data *aptd;
2341 
2342  /* Do not waste resources trying to play files that do not exist */
2343  if (ast_strlen_zero(filename)) {
2344  if (say_number < 0) {
2345  return 0;
2346  }
2347  } else if (!sound_file_exists(filename)) {
2348  return 0;
2349  }
2350 
2351  aptd = async_playback_task_data_alloc(conference, filename, say_number, initiator);
2352  if (!aptd) {
2353  return -1;
2354  }
2355 
2356  if (ast_taskprocessor_push(conference->playback_queue, async_playback_task, aptd)) {
2357  if (!ast_strlen_zero(filename)) {
2358  ast_log(LOG_WARNING, "Unable to play file '%s' to conference '%s'\n",
2359  filename, conference->name);
2360  } else {
2361  ast_log(LOG_WARNING, "Unable to say number '%d' to conference '%s'\n",
2362  say_number, conference->name);
2363  }
2364  async_playback_task_data_destroy(aptd);
2365  return -1;
2366  }
2367 
2368  return 0;
2369 }
2370 
2372  const char *filename, struct ast_channel *initiator)
2373 {
2374  return async_play_sound_helper(conference, filename, -1, initiator);
2375 }
2376 
2378 {
2379  struct ast_datastore *async_datastore;
2380  struct async_datastore_data *add;
2381 
2382  ast_channel_lock(chan);
2383  async_datastore = ast_channel_datastore_find(chan, &async_datastore_info, NULL);
2384  ast_channel_unlock(chan);
2385  if (!async_datastore) {
2386  return;
2387  }
2388 
2389  add = async_datastore->data;
2390 
2391  ast_mutex_lock(&add->lock);
2392  add->wait = 0;
2393  ast_cond_signal(&add->cond);
2394  ast_mutex_unlock(&add->lock);
2395 }
2396 
2397 /*!
2398  * \brief Play number into the conference bridge
2399  *
2400  * \param conference The conference bridge to say the number into
2401  * \param say_number number to say
2402  *
2403  * \retval 0 success
2404  * \retval -1 failure
2405  */
2406 static int play_sound_number(struct confbridge_conference *conference, int say_number)
2407 {
2408  return play_sound_helper(conference, NULL, say_number);
2409 }
2410 
2411 static int conf_handle_talker_cb(struct ast_bridge_channel *bridge_channel, void *hook_pvt, int talking)
2412 {
2413  struct confbridge_user *user = hook_pvt;
2414  RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup);
2415  struct ast_json *talking_extras;
2416 
2417  conference = ao2_find(conference_bridges, user->conference->name, OBJ_KEY);
2418  if (!conference) {
2419  /* Remove the hook since the conference does not exist. */
2420  return -1;
2421  }
2422 
2423  ao2_lock(conference);
2424  user->talking = talking;
2425  ao2_unlock(conference);
2426 
2427  talking_extras = ast_json_pack("{s: s, s: b}",
2428  "talking_status", talking ? "on" : "off",
2429  "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN));
2430  if (!talking_extras) {
2431  return 0;
2432  }
2433 
2434  send_conf_stasis(conference, bridge_channel->chan, confbridge_talking_type(), talking_extras, 0);
2435  ast_json_unref(talking_extras);
2436  return 0;
2437 }
2438 
2439 static int conf_get_pin(struct ast_channel *chan, struct confbridge_user *user)
2440 {
2441  char pin_guess[MAX_PIN+1] = { 0, };
2442  const char *pin = user->u_profile.pin;
2443  char *tmp = pin_guess;
2444  int i, res;
2445  unsigned int len = MAX_PIN;
2446 
2447  /*
2448  * NOTE: We have not joined a conference yet so we have to use
2449  * the bridge profile requested by the user.
2450  */
2451 
2452  /* give them three tries to get the pin right */
2453  for (i = 0; i < 3; i++) {
2454  if (ast_app_getdata(chan,
2455  conf_get_sound(CONF_SOUND_GET_PIN, user->b_profile.sounds),
2456  tmp, len, 0) >= 0) {
2457  if (!strcasecmp(pin, pin_guess)) {
2458  return 0;
2459  }
2460  }
2461  ast_streamfile(chan,
2462  conf_get_sound(CONF_SOUND_INVALID_PIN, user->b_profile.sounds),
2463  ast_channel_language(chan));
2464  res = ast_waitstream(chan, AST_DIGIT_ANY);
2465  if (res > 0) {
2466  /* Account for digit already read during ivalid pin playback
2467  * resetting pin buf. */
2468  pin_guess[0] = res;
2469  pin_guess[1] = '\0';
2470  tmp = pin_guess + 1;
2471  len = MAX_PIN - 1;
2472  } else {
2473  /* reset pin buf as empty buffer. */
2474  tmp = pin_guess;
2475  len = MAX_PIN;
2476  }
2477  }
2478  return -1;
2479 }
2480 
2481 static int user_timeout(struct ast_bridge_channel *bridge_channel, void *ignore)
2482 {
2484  pbx_builtin_setvar_helper(bridge_channel->chan, "CONFBRIDGE_RESULT", "TIMEOUT");
2485  return -1;
2486 }
2487 
2488 static int conf_rec_name(struct confbridge_user *user, const char *conf_name)
2489 {
2490  char destdir[PATH_MAX];
2491  int res;
2492  int duration = 20;
2493 
2494  snprintf(destdir, sizeof(destdir), "%s/confbridge", ast_config_AST_SPOOL_DIR);
2495 
2496  if (ast_mkdir(destdir, 0777) != 0) {
2497  ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
2498  return -1;
2499  }
2500  snprintf(user->name_rec_location, sizeof(user->name_rec_location),
2501  "%s/confbridge-name-%s-%s", destdir,
2502  conf_name, ast_channel_uniqueid(user->chan));
2503 
2504  if (!(ast_test_flag(&user->u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE_REVIEW))) {
2505  res = ast_play_and_record(user->chan,
2506  "vm-rec-name",
2507  user->name_rec_location,
2508  10,
2509  "sln",
2510  &duration,
2511  NULL,
2512  ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE),
2513  0,
2514  NULL);
2515  } else {
2516  res = ast_record_review(user->chan,
2517  "vm-rec-name",
2518  user->name_rec_location,
2519  10,
2520  "sln",
2521  &duration,
2522  NULL);
2523  }
2524 
2525  if (res == -1) {
2526  ast_filedelete(user->name_rec_location, NULL);
2527  user->name_rec_location[0] = '\0';
2528  return -1;
2529  }
2530  return 0;
2531 }
2532 
2534  struct confbridge_conference *conference;
2535  char filename[0];
2536 };
2537 
2538 static struct async_delete_name_rec_task_data *async_delete_name_rec_task_data_alloc(
2539  struct confbridge_conference *conference, const char *filename)
2540 {
2541  struct async_delete_name_rec_task_data *atd;
2542 
2543  atd = ast_malloc(sizeof(*atd) + strlen(filename) + 1);
2544  if (!atd) {
2545  return NULL;
2546  }
2547 
2548  /* Safe */
2549  strcpy(atd->filename, filename);
2550  atd->conference = conference;
2551 
2552  return atd;
2553 }
2554 
2555 static void async_delete_name_rec_task_data_destroy(struct async_delete_name_rec_task_data *atd)
2556 {
2557  ast_free(atd);
2558 }
2559 
2560 /*!
2561  * \brief Delete user's name file asynchronously
2562  *
2563  * This runs in the playback queue taskprocessor. This ensures that
2564  * sound file is removed after playback is finished and not before.
2565  *
2566  * \param data An async_delete_name_rec_task_data
2567  * \return 0
2568  */
2569 static int async_delete_name_rec_task(void *data)
2570 {
2571  struct async_delete_name_rec_task_data *atd = data;
2572 
2573  ast_filedelete(atd->filename, NULL);
2574  ast_log(LOG_DEBUG, "Conference '%s' removed user name file '%s'\n",
2575  atd->conference->name, atd->filename);
2576 
2577  async_delete_name_rec_task_data_destroy(atd);
2578  return 0;
2579 }
2580 
2581 static int async_delete_name_rec(struct confbridge_conference *conference,
2582  const char *filename)
2583 {
2584  struct async_delete_name_rec_task_data *atd;
2585 
2586  if (ast_strlen_zero(filename)) {
2587  return 0;
2588  } else if (!sound_file_exists(filename)) {
2589  return 0;
2590  }
2591 
2592  atd = async_delete_name_rec_task_data_alloc(conference, filename);
2593  if (!atd) {
2594  return -1;
2595  }
2596 
2598  ast_log(LOG_WARNING, "Conference '%s' was unable to remove user name file '%s'\n",
2599  conference->name, filename);
2600  async_delete_name_rec_task_data_destroy(atd);
2601  return -1;
2602  }
2603 
2604  return 0;
2605 }
2606 
2607 static int join_callback(struct ast_bridge_channel *bridge_channel, void *ignore)
2608 {
2609  async_play_sound_ready(bridge_channel->chan);
2610  return 0;
2611 }
2612 
2614  struct confbridge_conference *conference;
2615  struct confbridge_user *user;
2616  enum ast_bridge_hook_type hook_type;
2617 };
2618 
2619 static int send_event_hook_callback(struct ast_bridge_channel *bridge_channel, void *data)
2620 {
2621  struct confbridge_hook_data *hook_data = data;
2622 
2623  if (hook_data->hook_type == AST_BRIDGE_HOOK_TYPE_JOIN) {
2624  send_join_event(hook_data->user, hook_data->conference);
2625  } else {
2626  send_leave_event(hook_data->user, hook_data->conference);
2627  }
2628 
2629  return 0;
2630 }
2631 
2632 /*! \brief The ConfBridge application */
2633 static int confbridge_exec(struct ast_channel *chan, const char *data)
2634 {
2635  int res = 0, volume_adjustments[2];
2636  int quiet = 0;
2637  int async_delete_task_pushed = 0;
2638  char *parse;
2639  const char *b_profile_name = NULL;
2640  const char *u_profile_name = NULL;
2641  const char *menu_profile_name = NULL;
2642  struct confbridge_conference *conference = NULL;
2643  struct confbridge_user user = {
2644  .chan = chan,
2645  .tech_args.talking_threshold = DEFAULT_TALKING_THRESHOLD,
2646  .tech_args.silence_threshold = DEFAULT_SILENCE_THRESHOLD,
2647  .tech_args.drop_silence = 0,
2648  };
2649  struct confbridge_hook_data *join_hook_data;
2650  struct confbridge_hook_data *leave_hook_data;
2651 
2652  AST_DECLARE_APP_ARGS(args,
2653  AST_APP_ARG(conf_name);
2654  AST_APP_ARG(b_profile_name);
2655  AST_APP_ARG(u_profile_name);
2656  AST_APP_ARG(menu_profile_name);
2657  );
2658 
2659  if (ast_bridge_features_init(&user.features)) {
2660  pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2661  res = -1;
2662  goto confbridge_cleanup;
2663  }
2664 
2665  /* We need to make a copy of the input string if we are going to modify it! */
2666  parse = ast_strdupa(data);
2667 
2668  AST_STANDARD_APP_ARGS(args, parse);
2669 
2670  if (ast_strlen_zero(args.conf_name)) {
2671  pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2672  ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
2673  res = -1;
2674  goto confbridge_cleanup;
2675  }
2676 
2677  if (strlen(args.conf_name) >= MAX_CONF_NAME) {
2678  pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2679  ast_log(LOG_WARNING, "%s does not accept conference names longer than %d\n", app, MAX_CONF_NAME - 1);
2680  res = -1;
2681  goto confbridge_cleanup;
2682  }
2683 
2684  /* bridge profile name */
2685  if (args.argc > 1 && !ast_strlen_zero(args.b_profile_name)) {
2686  b_profile_name = args.b_profile_name;
2687  }
2688  if (!conf_find_bridge_profile(chan, b_profile_name, &user.b_profile)) {
2689  pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2690  ast_log(LOG_WARNING, "Conference bridge profile %s does not exist\n", b_profile_name ?
2691  b_profile_name : DEFAULT_BRIDGE_PROFILE);
2692  res = -1;
2693  goto confbridge_cleanup;
2694  }
2695 
2696  /* user profile name */
2697  if (args.argc > 2 && !ast_strlen_zero(args.u_profile_name)) {
2698  u_profile_name = args.u_profile_name;
2699  }
2700  if (!conf_find_user_profile(chan, u_profile_name, &user.u_profile)) {
2701  pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2702  ast_log(LOG_WARNING, "Conference user profile %s does not exist\n", u_profile_name ?
2703  u_profile_name : DEFAULT_USER_PROFILE);
2704  res = -1;
2705  goto confbridge_cleanup;
2706  }
2707 
2708  /* If channel hasn't been answered already, answer it, unless we're explicitly not supposed to */
2709  if ((ast_channel_state(chan) != AST_STATE_UP) && (ast_test_flag(&user.u_profile, USER_OPT_ANSWER_CHANNEL))) {
2710  ast_answer(chan);
2711  }
2712 
2713  quiet = ast_test_flag(&user.u_profile, USER_OPT_QUIET);
2714 
2715  /* ask for a PIN immediately after finding user profile. This has to be
2716  * prompted for requardless of quiet setting. */
2717  if (!ast_strlen_zero(user.u_profile.pin)) {
2718  if (conf_get_pin(chan, &user)) {
2719  pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2720  res = -1; /* invalid PIN */
2721  goto confbridge_cleanup;
2722  }
2723  }
2724 
2725  /* See if we need them to record a intro name */
2726  if (!quiet &&
2727  (ast_test_flag(&user.u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE) ||
2728  (ast_test_flag(&user.u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE_REVIEW)))) {
2729  if (conf_rec_name(&user, args.conf_name)) {
2730  pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2731  res = -1; /* Hangup during name recording */
2732  goto confbridge_cleanup;
2733  }
2734  }
2735 
2736  /* menu name */
2737  if (args.argc > 3 && !ast_strlen_zero(args.menu_profile_name)) {
2738  menu_profile_name = args.menu_profile_name;
2739  }
2740 
2741  if (conf_set_menu_to_user(chan, &user, menu_profile_name)) {
2742  pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2743  ast_log(LOG_WARNING, "Conference menu profile %s does not exist\n", menu_profile_name ?
2744  menu_profile_name : DEFAULT_MENU_PROFILE);
2745  res = -1;
2746  goto confbridge_cleanup;
2747  }
2748 
2749  /* Set if DTMF should pass through for this user or not */
2750  if (ast_test_flag(&user.u_profile, USER_OPT_DTMF_PASS)) {
2751  user.features.dtmf_passthrough = 1;
2752  } else {
2753  user.features.dtmf_passthrough = 0;
2754  }
2755 
2756  /* Set if text messaging is enabled for this user or not */
2757  if (ast_test_flag(&user.u_profile, USER_OPT_TEXT_MESSAGING)) {
2758  user.features.text_messaging = 1;
2759  } else {
2760  user.features.text_messaging = 0;
2761  }
2762 
2763  /* Set dsp threshold values if present */
2764  if (user.u_profile.talking_threshold) {
2766  }
2767  if (user.u_profile.silence_threshold) {
2769  }
2770 
2771  /* Set a talker indicate call back if talking detection is requested */
2772  if (ast_test_flag(&user.u_profile, USER_OPT_TALKER_DETECT)) {
2773  if (ast_bridge_talk_detector_hook(&user.features, conf_handle_talker_cb,
2774  &user, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
2775  pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2776  res = -1;
2777  goto confbridge_cleanup;
2778  }
2779  }
2780 
2781  /* Look for a conference bridge matching the provided name */
2782  if (!(conference = join_conference_bridge(args.conf_name, &user))) {
2783  pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2784  res = -1;
2785  goto confbridge_cleanup;
2786  }
2787 
2788  /* Keep a copy of volume adjustments so we can restore them later if need be */
2789  volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
2790  volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
2791 
2792  if (ast_test_flag(&user.u_profile, USER_OPT_DROP_SILENCE)) {
2793  user.tech_args.drop_silence = 1;
2794  }
2795 
2796  if (ast_test_flag(&user.u_profile, USER_OPT_JITTERBUFFER)) {
2797  ast_func_write(chan, "JITTERBUFFER(adaptive)", "default");
2798  }
2799 
2800  if (ast_test_flag(&user.u_profile, USER_OPT_DENOISE)) {
2801  ast_func_write(chan, "DENOISE(rx)", "on");
2802  }
2803 
2804  /* if this user has a intro, play it before entering */
2805  if (!ast_strlen_zero(user.name_rec_location)) {
2806  ast_autoservice_start(chan);
2807  play_sound_file(conference, user.name_rec_location);
2808  play_sound_file(conference,
2809  conf_get_sound(CONF_SOUND_HAS_JOINED, conference->b_profile.sounds));
2810  ast_autoservice_stop(chan);
2811  }
2812 
2813  if (!quiet) {
2814  const char *join_sound = conf_get_sound(CONF_SOUND_JOIN, conference->b_profile.sounds);
2815 
2816  /* if hear_own_join_sound is enabled play the Join sound to everyone */
2817  if (ast_test_flag(&user.u_profile, USER_OPT_HEAR_OWN_JOIN_SOUND) ) {
2818  if (strcmp(conference->b_profile.language, ast_channel_language(chan))) {
2819  ast_stream_and_wait(chan, join_sound, "");
2820  ast_autoservice_start(chan);
2821  play_sound_file(conference, join_sound);
2822  ast_autoservice_stop(chan);
2823  } else {
2824  async_play_sound_file(conference, join_sound, chan);
2825  }
2826  /* if hear_own_join_sound is disabled only play the Join sound to just the conference */
2827  } else {
2828  ast_autoservice_start(chan);
2829  play_sound_file(conference, join_sound);
2830  ast_autoservice_stop(chan);
2831  }
2832  }
2833 
2834  if (user.u_profile.timeout) {
2836  0,
2837  user.u_profile.timeout * 1000,
2838  user_timeout,
2839  NULL,
2840  NULL,
2842  }
2843 
2844  /* See if we need to automatically set this user as a video source or not */
2845  handle_video_on_join(conference, user.chan, ast_test_flag(&user.u_profile, USER_OPT_MARKEDUSER));
2846 
2847  conf_moh_unsuspend(&user);
2848 
2849  join_hook_data = ast_malloc(sizeof(*join_hook_data));
2850  if (!join_hook_data) {
2851  res = -1;
2852  goto confbridge_cleanup;
2853  }
2854  join_hook_data->user = &user;
2855  join_hook_data->conference = conference;
2856  join_hook_data->hook_type = AST_BRIDGE_HOOK_TYPE_JOIN;
2857  res = ast_bridge_join_hook(&user.features, send_event_hook_callback,
2858  join_hook_data, ast_free_ptr, 0);
2859  if (res) {
2860  ast_free(join_hook_data);
2861  ast_log(LOG_ERROR, "Couldn't add bridge join hook for channel '%s'\n", ast_channel_name(chan));
2862  goto confbridge_cleanup;
2863  }
2864 
2865  leave_hook_data = ast_malloc(sizeof(*leave_hook_data));
2866  if (!leave_hook_data) {
2867  /* join_hook_data is cleaned up by ast_bridge_features_cleanup via the goto */
2868  res = -1;
2869  goto confbridge_cleanup;
2870  }
2871  leave_hook_data->user = &user;
2872  leave_hook_data->conference = conference;
2873  leave_hook_data->hook_type = AST_BRIDGE_HOOK_TYPE_LEAVE;
2874  res = ast_bridge_leave_hook(&user.features, send_event_hook_callback,
2875  leave_hook_data, ast_free_ptr, 0);
2876  if (res) {
2877  /* join_hook_data is cleaned up by ast_bridge_features_cleanup via the goto */
2878  ast_free(leave_hook_data);
2879  ast_log(LOG_ERROR, "Couldn't add bridge leave hook for channel '%s'\n", ast_channel_name(chan));
2880  goto confbridge_cleanup;
2881  }
2882 
2883  if (ast_bridge_join_hook(&user.features, join_callback, NULL, NULL, 0)) {
2885  }
2886 
2887  ast_bridge_join(conference->bridge,
2888  chan,
2889  NULL,
2890  &user.features,
2891  &user.tech_args,
2892  0);
2893 
2894  /* This is a catch-all in case joining the bridge failed or for some reason
2895  * an async announcement got queued up and hasn't been told to play yet
2896  */
2897  async_play_sound_ready(chan);
2898 
2899  if (!user.kicked && ast_check_hangup(chan)) {
2900  pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "HANGUP");
2901  }
2902 
2903  /* if we're shutting down, don't attempt to do further processing */
2904  if (ast_shutting_down()) {
2905  /*
2906  * Not taking any new calls at this time. We cannot create
2907  * the announcer channel if this is the first channel into
2908  * the conference and we certainly cannot create any
2909  * recording channel.
2910  */
2911  leave_conference(&user);
2912  conference = NULL;
2913  goto confbridge_cleanup;
2914  }
2915 
2916  /* If this user was a video source, we need to clean up and possibly pick a new source. */
2917  handle_video_on_exit(conference, user.chan);
2918 
2919  /* if this user has a intro, play it when leaving */
2920  if (!quiet && !ast_strlen_zero(user.name_rec_location)) {
2921  async_play_sound_file(conference, user.name_rec_location, NULL);
2922  async_play_sound_file(conference,
2923  conf_get_sound(CONF_SOUND_HAS_LEFT, conference->b_profile.sounds), NULL);
2924  async_delete_name_rec(conference, user.name_rec_location);
2925  async_delete_task_pushed = 1;
2926  }
2927 
2928  /* play the leave sound */
2929  if (!quiet) {
2930  const char *leave_sound = conf_get_sound(CONF_SOUND_LEAVE, conference->b_profile.sounds);
2931  async_play_sound_file(conference, leave_sound, NULL);
2932  }
2933 
2934  /* If the user was kicked from the conference play back the audio prompt for it */
2935  if (!quiet && user.kicked) {
2936  res = ast_stream_and_wait(chan,
2937  conf_get_sound(CONF_SOUND_KICKED, conference->b_profile.sounds),
2938  "");
2939  }
2940 
2941  /* Easy as pie, depart this channel from the conference bridge */
2942  leave_conference(&user);
2943  conference = NULL;
2944 
2945  /* Restore volume adjustments to previous values in case they were changed */
2946  if (volume_adjustments[0]) {
2947  ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
2948  }
2949  if (volume_adjustments[1]) {
2950  ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
2951  }
2952 
2953 confbridge_cleanup:
2954  if (!async_delete_task_pushed && !ast_strlen_zero(user.name_rec_location)) {
2955  ast_filedelete(user.name_rec_location, NULL);
2956  }
2959  return res;
2960 }
2961 
2962 static int action_toggle_mute(struct confbridge_conference *conference,
2963  struct confbridge_user *user,
2964  struct ast_bridge_channel *bridge_channel)
2965 {
2966  int mute;
2967 
2968  /* Toggle user level mute request. */
2969  mute = !user->muted;
2970  generic_mute_unmute_user(conference, user, mute);
2971 
2972  return play_file(bridge_channel, NULL,
2973  conf_get_sound(mute ? CONF_SOUND_MUTED : CONF_SOUND_UNMUTED,
2974  conference->b_profile.sounds)) < 0;
2975 }
2976 
2977 static int action_toggle_binaural(struct confbridge_conference *conference,
2978  struct confbridge_user *user,
2979  struct ast_bridge_channel *bridge_channel)
2980 {
2981  unsigned int binaural;
2982  ast_bridge_channel_lock_bridge(bridge_channel);
2983  binaural = !bridge_channel->binaural_suspended;
2984  bridge_channel->binaural_suspended = binaural;
2985  ast_bridge_unlock(bridge_channel->bridge);
2986  return play_file(bridge_channel, NULL, (binaural ?
2987  conf_get_sound(CONF_SOUND_BINAURAL_OFF, user->b_profile.sounds) :
2988  conf_get_sound(CONF_SOUND_BINAURAL_ON, user->b_profile.sounds))) < 0;
2989 }
2990 
2991 static int action_toggle_mute_participants(struct confbridge_conference *conference, struct confbridge_user *user)
2992 {
2993  struct confbridge_user *cur_user = NULL;
2994  const char *sound_to_play;
2995  int mute;
2996 
2997  ao2_lock(conference);
2998 
2999  /* Toggle bridge level mute request. */
3000  mute = !conference->muted;
3001  conference->muted = mute;
3002 
3003  AST_LIST_TRAVERSE(&conference->active_list, cur_user, list) {
3004  if (!ast_test_flag(&cur_user->u_profile, USER_OPT_ADMIN)) {
3005  /* Set user level to bridge level mute request. */
3006  cur_user->muted = mute;
3007  conf_update_user_mute(cur_user);
3008  }
3009  }
3010 
3011  ao2_unlock(conference);
3012 
3013  sound_to_play = conf_get_sound(
3014  mute ? CONF_SOUND_PARTICIPANTS_MUTED : CONF_SOUND_PARTICIPANTS_UNMUTED,
3015  conference->b_profile.sounds);
3016 
3017  if (strcmp(conference->b_profile.language, ast_channel_language(user->chan))) {
3018  /* The host needs to hear it seperately, as they don't get the audio from play_sound_helper */
3019  ast_stream_and_wait(user->chan, sound_to_play, "");
3020 
3021  /* Announce to the group that all participants are muted */
3022  ast_autoservice_start(user->chan);
3023  play_sound_file(conference, sound_to_play);
3024  ast_autoservice_stop(user->chan);
3025  } else {
3026  /* Playing the sound asynchronously lets the sound be heard by everyone at once */
3027  async_play_sound_file(conference, sound_to_play, user->chan);
3028  }
3029 
3030  return 0;
3031 }
3032 
3033 static int action_playback(struct ast_bridge_channel *bridge_channel, const char *playback_file)
3034 {
3035  char *file_copy = ast_strdupa(playback_file);
3036  char *file = NULL;
3037 
3038  while ((file = ast_strsep(&file_copy, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
3039  if (ast_stream_and_wait(bridge_channel->chan, file, "")) {
3040  ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
3041  return -1;
3042  }
3043  }
3044  return 0;
3045 }
3046 
3047 static int action_playback_and_continue(struct confbridge_conference *conference,
3048  struct confbridge_user *user,
3049  struct ast_bridge_channel *bridge_channel,
3050  struct conf_menu *menu,
3051  const char *playback_file,
3052  const char *cur_dtmf,
3053  int *stop_prompts)
3054 {
3055  int i;
3056  int digit = 0;
3057  char dtmf[MAXIMUM_DTMF_FEATURE_STRING];
3058  struct conf_menu_entry new_menu_entry = { { 0, }, };
3059  char *file_copy = ast_strdupa(playback_file);
3060  char *file = NULL;
3061 
3062  while ((file = ast_strsep(&file_copy, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
3063  if (ast_streamfile(bridge_channel->chan, file, ast_channel_language(bridge_channel->chan))) {
3064  ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
3065  return -1;
3066  }
3067 
3068  /* now wait for more digits. */
3069  if (!(digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY))) {
3070  /* streaming finished and no DTMF was entered */
3071  continue;
3072  } else if (digit == -1) {
3073  /* error */
3074  return -1;
3075  } else {
3076  break; /* dtmf was entered */
3077  }
3078  }
3079  if (!digit) {
3080  /* streaming finished on all files and no DTMF was entered */
3081  return -1;
3082  }
3083  ast_stopstream(bridge_channel->chan);
3084 
3085  /* If we get here, then DTMF has been entered, This means no
3086  * additional prompts should be played for this menu entry */
3087  *stop_prompts = 1;
3088 
3089  /* If a digit was pressed during the payback, update
3090  * the dtmf string and look for a new menu entry in the
3091  * menu structure */
3092  ast_copy_string(dtmf, cur_dtmf, sizeof(dtmf));
3093  for (i = 0; i < (MAXIMUM_DTMF_FEATURE_STRING - 1); i++) {
3094  dtmf[i] = cur_dtmf[i];
3095  if (!dtmf[i]) {
3096  dtmf[i] = (char) digit;
3097  dtmf[i + 1] = '\0';
3098  i = -1;
3099  break;
3100  }
3101  }
3102  /* If i is not -1 then the new dtmf digit was _NOT_ added to the string.
3103  * If this is the case, no new DTMF sequence should be looked for. */
3104  if (i != -1) {
3105  return 0;
3106  }
3107 
3108  if (conf_find_menu_entry_by_sequence(dtmf, menu, &new_menu_entry)) {
3109  execute_menu_entry(conference,
3110  user,
3111  bridge_channel,
3112  &new_menu_entry, menu);
3113  conf_menu_entry_destroy(&new_menu_entry);
3114  }
3115  return 0;
3116 }
3117 
3118 static int action_kick_last(struct confbridge_conference *conference,
3119  struct ast_bridge_channel *bridge_channel,
3120  struct confbridge_user *user)
3121 {
3122  struct confbridge_user *last_user = NULL;
3123  int isadmin = ast_test_flag(&user->u_profile, USER_OPT_ADMIN);
3124 
3125  if (!isadmin) {
3126  play_file(bridge_channel, NULL,
3127  conf_get_sound(CONF_SOUND_ERROR_MENU, conference->b_profile.sounds));
3128  ast_log(LOG_WARNING, "Only admin users can use the kick_last menu action. Channel %s of conf %s is not an admin.\n",
3129  ast_channel_name(bridge_channel->chan),
3130  conference->name);
3131  return -1;
3132  }
3133 
3134  ao2_lock(conference);
3135  last_user = AST_LIST_LAST(&conference->active_list);
3136  if (!last_user) {
3137  ao2_unlock(conference);
3138  return 0;
3139  }
3140 
3141  if (last_user == user || ast_test_flag(&last_user->u_profile, USER_OPT_ADMIN)) {
3142  ao2_unlock(conference);
3143  play_file(bridge_channel, NULL,
3144  conf_get_sound(CONF_SOUND_ERROR_MENU, conference->b_profile.sounds));
3145  } else if (!last_user->kicked) {
3146  last_user->kicked = 1;
3147  pbx_builtin_setvar_helper(last_user->chan, "CONFBRIDGE_RESULT", "KICKED");
3148  ast_bridge_remove(conference->bridge, last_user->chan);
3149  ao2_unlock(conference);
3150  }
3151 
3152  return 0;
3153 }
3154 
3155 static int action_dialplan_exec(struct ast_bridge_channel *bridge_channel, struct conf_menu_action *menu_action)
3156 {
3157  struct ast_pbx_args args;
3158  struct ast_pbx *pbx;
3159  char *exten;
3160  char *context;
3161  int priority;
3162  int res;
3163 
3164  memset(&args, 0, sizeof(args));
3165  args.no_hangup_chan = 1;
3166 
3167  ast_channel_lock(bridge_channel->chan);
3168 
3169  /*save off*/
3170  exten = ast_strdupa(ast_channel_exten(bridge_channel->chan));
3171  context = ast_strdupa(ast_channel_context(bridge_channel->chan));
3172  priority = ast_channel_priority(bridge_channel->chan);
3173  pbx = ast_channel_pbx(bridge_channel->chan);
3174  ast_channel_pbx_set(bridge_channel->chan, NULL);
3175 
3176  /*set new*/
3177  ast_channel_exten_set(bridge_channel->chan, menu_action->data.dialplan_args.exten);
3178  ast_channel_context_set(bridge_channel->chan, menu_action->data.dialplan_args.context);
3179  ast_channel_priority_set(bridge_channel->chan, menu_action->data.dialplan_args.priority);
3180 
3181  ast_channel_unlock(bridge_channel->chan);
3182 
3183  /*execute*/
3184  res = ast_pbx_run_args(bridge_channel->chan, &args);
3185 
3186  /*restore*/
3187  ast_channel_lock(bridge_channel->chan);
3188 
3189  ast_channel_exten_set(bridge_channel->chan, exten);
3190  ast_channel_context_set(bridge_channel->chan, context);
3191  ast_channel_priority_set(bridge_channel->chan, priority);
3192  ast_channel_pbx_set(bridge_channel->chan, pbx);
3193 
3194  ast_channel_unlock(bridge_channel->chan);
3195 
3196  return res;
3197 }
3198 
3199 static int execute_menu_entry(struct confbridge_conference *conference,
3200  struct confbridge_user *user,
3201  struct ast_bridge_channel *bridge_channel,
3202  struct conf_menu_entry *menu_entry,
3203  struct conf_menu *menu)
3204 {
3205  struct conf_menu_action *menu_action;
3206  int isadmin = ast_test_flag(&user->u_profile, USER_OPT_ADMIN);
3207  int stop_prompts = 0;
3208  int res = 0;
3209 
3210  AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
3211  switch (menu_action->id) {
3212  case MENU_ACTION_TOGGLE_MUTE:
3213  res |= action_toggle_mute(conference, user, bridge_channel);
3214  break;
3215  case MENU_ACTION_TOGGLE_BINAURAL:
3216  action_toggle_binaural(conference, user, bridge_channel);
3217  break;
3218  case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
3219  if (!isadmin) {
3220  break;
3221  }
3222  action_toggle_mute_participants(conference, user);
3223  break;
3224  case MENU_ACTION_PARTICIPANT_COUNT:
3225  announce_user_count(conference, user, bridge_channel);
3226  break;
3227  case MENU_ACTION_PLAYBACK:
3228  if (!stop_prompts) {
3229  res |= action_playback(bridge_channel, menu_action->data.playback_file);
3230  ast_test_suite_event_notify("CONF_MENU_PLAYBACK",
3231  "Message: %s\r\nChannel: %s",
3232  menu_action->data.playback_file, ast_channel_name(bridge_channel->chan));
3233  }
3234  break;
3235  case MENU_ACTION_RESET_LISTENING:
3237  break;
3238  case MENU_ACTION_RESET_TALKING:
3240  break;
3241  case MENU_ACTION_INCREASE_LISTENING:
3244  break;
3245  case MENU_ACTION_DECREASE_LISTENING:
3248  break;
3249  case MENU_ACTION_INCREASE_TALKING:
3252  break;
3253  case MENU_ACTION_DECREASE_TALKING:
3256  break;
3257  case MENU_ACTION_PLAYBACK_AND_CONTINUE:
3258  if (!(stop_prompts)) {
3259  res |= action_playback_and_continue(conference,
3260  user,
3261  bridge_channel,
3262  menu,
3263  menu_action->data.playback_file,
3264  menu_entry->dtmf,
3265  &stop_prompts);
3266  }
3267  break;
3268  case MENU_ACTION_DIALPLAN_EXEC:
3269  res |= action_dialplan_exec(bridge_channel, menu_action);
3270  break;
3271  case MENU_ACTION_ADMIN_TOGGLE_LOCK:
3272  if (!isadmin) {
3273  break;
3274  }
3275  conference->locked = (!conference->locked ? 1 : 0);
3276  res |= play_file(bridge_channel, NULL,
3278  conference->locked ? CONF_SOUND_LOCKED_NOW : CONF_SOUND_UNLOCKED_NOW,
3279  conference->b_profile.sounds)) < 0;
3280  break;
3281  case MENU_ACTION_ADMIN_KICK_LAST:
3282  res |= action_kick_last(conference, bridge_channel, user);
3283  break;
3284  case MENU_ACTION_LEAVE:
3285  pbx_builtin_setvar_helper(bridge_channel->chan, "CONFBRIDGE_RESULT", "DTMF");
3286  ao2_lock(conference);
3287  ast_bridge_remove(conference->bridge, bridge_channel->chan);
3288  ast_test_suite_event_notify("CONF_MENU_LEAVE",
3289  "Channel: %s",
3290  ast_channel_name(bridge_channel->chan));
3291  ao2_unlock(conference);
3292  break;
3293  case MENU_ACTION_NOOP:
3294  break;
3295  case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
3296  ao2_lock(conference);
3297  if (!ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_SFU)) {
3298  ast_bridge_set_single_src_video_mode(conference->bridge, bridge_channel->chan);
3299  }
3300  ao2_unlock(conference);
3301  break;
3302  case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
3303  handle_video_on_exit(conference, bridge_channel->chan);
3304  break;
3305  }
3306  }
3307  return res;
3308 }
3309 
3310 int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel,
3311  struct confbridge_user *user,
3312  struct conf_menu_entry *menu_entry,
3313  struct conf_menu *menu)
3314 {
3315  /* See if music on hold is playing */
3316  conf_moh_suspend(user);
3317 
3318  /* execute the list of actions associated with this menu entry */
3319  execute_menu_entry(user->conference, user, bridge_channel, menu_entry, menu);
3320 
3321  /* See if music on hold needs to be started back up again */
3322  conf_moh_unsuspend(user);
3323 
3324  async_play_sound_ready(bridge_channel->chan);
3325 
3326  return 0;
3327 }
3328 
3329 static int kick_conference_participant(struct confbridge_conference *conference,
3330  const char *channel)
3331 {
3332  int res = -1;
3333  int match;
3334  struct confbridge_user *user = NULL;
3335  int all = !strcasecmp("all", channel);
3336  int participants = !strcasecmp("participants", channel);
3337 
3338  SCOPED_AO2LOCK(bridge_lock, conference);
3339 
3340  AST_LIST_TRAVERSE(&conference->active_list, user, list) {
3341  if (user->kicked) {
3342  continue;
3343  }
3344  match = !strcasecmp(channel, ast_channel_name(user->chan));
3345  if (match || all
3346  || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
3347  user->kicked = 1;
3348  pbx_builtin_setvar_helper(user->chan, "CONFBRIDGE_RESULT", "KICKED");
3349  ast_bridge_remove(conference->bridge, user->chan);
3350  res = 0;
3351  if (match) {
3352  return res;
3353  }
3354  }
3355  }
3356  AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
3357  if (user->kicked) {
3358  continue;
3359  }
3360  match = !strcasecmp(channel, ast_channel_name(user->chan));
3361  if (match || all
3362  || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
3363  user->kicked = 1;
3364  pbx_builtin_setvar_helper(user->chan, "CONFBRIDGE_RESULT", "KICKED");
3365  ast_bridge_remove(conference->bridge, user->chan);
3366  res = 0;
3367  if (match) {
3368  return res;
3369  }
3370  }
3371  }
3372 
3373  return res;
3374 }
3375 
3376 static char *complete_confbridge_name(const char *line, const char *word, int pos, int state)
3377 {
3378  int which = 0;
3379  struct confbridge_conference *conference;
3380  char *res = NULL;
3381  int wordlen = strlen(word);
3382  struct ao2_iterator iter;
3383 
3384  iter = ao2_iterator_init(conference_bridges, 0);
3385  while ((conference = ao2_iterator_next(&iter))) {
3386  if (!strncasecmp(conference->name, word, wordlen) && ++which > state) {
3387  res = ast_strdup(conference->name);
3388  ao2_ref(conference, -1);
3389  break;
3390  }
3391  ao2_ref(conference, -1);
3392  }
3393  ao2_iterator_destroy(&iter);
3394 
3395  return res;
3396 }
3397 
3398 static char *complete_confbridge_participant(const char *conference_name, const char *line, const char *word, int pos, int state)
3399 {
3400  int which = 0;
3401  RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup);
3402  struct confbridge_user *user;
3403  char *res = NULL;
3404  int wordlen = strlen(word);
3405 
3406  conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
3407  if (!conference) {
3408  return NULL;
3409  }
3410 
3411  if (!strncasecmp("all", word, wordlen) && ++which > state) {
3412  return ast_strdup("all");
3413  }
3414 
3415  if (!strncasecmp("participants", word, wordlen) && ++which > state) {
3416  return ast_strdup("participants");
3417  }
3418 
3419  {
3420  SCOPED_AO2LOCK(bridge_lock, conference);
3421  AST_LIST_TRAVERSE(&conference->active_list, user, list) {
3422  if (!strncasecmp(ast_channel_name(user->chan), word, wordlen) && ++which > state) {
3423  res = ast_strdup(ast_channel_name(user->chan));
3424  return res;
3425  }
3426  }
3427  AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
3428  if (!strncasecmp(ast_channel_name(user->chan), word, wordlen) && ++which > state) {
3429  res = ast_strdup(ast_channel_name(user->chan));
3430  return res;
3431  }
3432  }
3433  }
3434 
3435  return NULL;
3436 }
3437 
3438 static char *handle_cli_confbridge_kick(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3439 {
3440  struct confbridge_conference *conference;
3441  int not_found;
3442 
3443  switch (cmd) {
3444  case CLI_INIT:
3445  e->command = "confbridge kick";
3446  e->usage =
3447  "Usage: confbridge kick <conference> <channel>\n"
3448  " Kicks a channel out of the conference bridge.\n"
3449  " (all to kick everyone, participants to kick non-admins).\n";
3450  return NULL;
3451  case CLI_GENERATE:
3452  if (a->pos == 2) {
3453  return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3454  }
3455  if (a->pos == 3) {
3456  return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
3457  }
3458  return NULL;
3459  }
3460 
3461  if (a->argc != 4) {
3462  return CLI_SHOWUSAGE;
3463  }
3464 
3465  conference = ao2_find(conference_bridges, a->argv[2], OBJ_KEY);
3466  if (!conference) {
3467  ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
3468  return CLI_SUCCESS;
3469  }
3470  not_found = kick_conference_participant(conference, a->argv[3]);
3471  ao2_ref(conference, -1);
3472  if (not_found) {
3473  if (!strcasecmp("all", a->argv[3]) || !strcasecmp("participants", a->argv[3])) {
3474  ast_cli(a->fd, "No participants found!\n");
3475  } else {
3476  ast_cli(a->fd, "No participant named '%s' found!\n", a->argv[3]);
3477  }
3478  return CLI_SUCCESS;
3479  }
3480  ast_cli(a->fd, "Kicked '%s' out of conference '%s'\n", a->argv[3], a->argv[2]);
3481  return CLI_SUCCESS;
3482 }
3483 
3484 static void handle_cli_confbridge_list_item(struct ast_cli_args *a, struct confbridge_user *user, int waiting)
3485 {
3486  char flag_str[6 + 1];/* Max flags + terminator */
3487  int pos = 0;
3488 
3489  /* Build flags column string. */
3490  if (ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
3491  flag_str[pos++] = 'A';
3492  }
3493  if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
3494  flag_str[pos++] = 'M';
3495  }
3496  if (ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)) {
3497  flag_str[pos++] = 'W';
3498  }
3499  if (ast_test_flag(&user->u_profile, USER_OPT_ENDMARKED)) {
3500  flag_str[pos++] = 'E';
3501  }
3502  if (user->muted) {
3503  flag_str[pos++] = 'm';
3504  }
3505  if (waiting) {
3506  flag_str[pos++] = 'w';
3507  }
3508  flag_str[pos] = '\0';
3509 
3510  ast_cli(a->fd, "%-30s %-6s %-16s %-16s %-16s %s\n",
3511  ast_channel_name(user->chan),
3512  flag_str,
3513  user->u_profile.name,
3514  user->conference->b_profile.name,
3515  user->menu_name,
3516  S_COR(ast_channel_caller(user->chan)->id.number.valid,
3517  ast_channel_caller(user->chan)->id.number.str, "<unknown>"));
3518 }
3519 
3520 static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3521 {
3522  struct confbridge_conference *conference;
3523 
3524  switch (cmd) {
3525  case CLI_INIT:
3526  e->command = "confbridge list";
3527  e->usage =
3528  "Usage: confbridge list [<name>]\n"
3529  " Lists all currently active conference bridges or a specific conference bridge.\n"
3530  "\n"
3531  " When a conference bridge name is provided, flags may be shown for users. Below\n"
3532  " are the flags and what they represent.\n"
3533  "\n"
3534  " Flags:\n"
3535  " A - The user is an admin\n"
3536  " M - The user is a marked user\n"
3537  " W - The user must wait for a marked user to join\n"
3538  " E - The user will be kicked after the last marked user leaves the conference\n"
3539  " m - The user is muted\n"
3540  " w - The user is waiting for a marked user to join\n";
3541  return NULL;
3542  case CLI_GENERATE:
3543  if (a->pos == 2) {
3544  return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3545  }
3546  return NULL;
3547  }
3548 
3549  if (a->argc == 2) {
3550  struct ao2_iterator iter;
3551 
3552  ast_cli(a->fd, "Conference Bridge Name Users Marked Locked Muted\n");
3553  ast_cli(a->fd, "================================ ====== ====== ====== =====\n");
3554  iter = ao2_iterator_init(conference_bridges, 0);
3555  while ((conference = ao2_iterator_next(&iter))) {
3556  ast_cli(a->fd, "%-32s %6u %6u %-6s %s\n",
3557  conference->name,
3558  conference->activeusers + conference->waitingusers,
3559  conference->markedusers,
3560  AST_CLI_YESNO(conference->locked),
3561  AST_CLI_YESNO(conference->muted));
3562  ao2_ref(conference, -1);
3563  }
3564  ao2_iterator_destroy(&iter);
3565  return CLI_SUCCESS;
3566  }
3567 
3568  if (a->argc == 3) {
3569  struct confbridge_user *user;
3570 
3571  conference = ao2_find(conference_bridges, a->argv[2], OBJ_KEY);
3572  if (!conference) {
3573  ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
3574  return CLI_SUCCESS;
3575  }
3576  ast_cli(a->fd, "Channel Flags User Profile Bridge Profile Menu CallerID\n");
3577  ast_cli(a->fd, "============================== ====== ================ ================ ================ ================\n");
3578  ao2_lock(conference);
3579  AST_LIST_TRAVERSE(&conference->active_list, user, list) {
3580  handle_cli_confbridge_list_item(a, user, 0);
3581  }
3582  AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
3583  handle_cli_confbridge_list_item(a, user, 1);
3584  }
3585  ao2_unlock(conference);
3586  ao2_ref(conference, -1);
3587  return CLI_SUCCESS;
3588  }
3589 
3590  return CLI_SHOWUSAGE;
3591 }
3592 
3593 /*! \internal
3594  * \brief finds a conference by name and locks/unlocks.
3595  *
3596  * \retval 0 success
3597  * \retval -1 conference not found
3598  */
3599 static int generic_lock_unlock_helper(int lock, const char *conference_name)
3600 {
3601  struct confbridge_conference *conference;
3602  int res = 0;
3603 
3604  conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
3605  if (!conference) {
3606  return -1;
3607  }
3608  ao2_lock(conference);
3609  conference->locked = lock;
3610  ast_test_suite_event_notify("CONF_LOCK", "Message: conference %s\r\nConference: %s", conference->locked ? "locked" : "unlocked", conference->b_profile.name);
3611  ao2_unlock(conference);
3612  ao2_ref(conference, -1);
3613 
3614  return res;
3615 }
3616 
3617 /*! \internal
3618  * \brief finds a conference user by channel name and mutes/unmutes them.
3619  *
3620  * \retval 0 success
3621  * \retval -1 conference not found
3622  * \retval -2 user not found
3623  */
3624 static int generic_mute_unmute_helper(int mute, const char *conference_name,
3625  const char *chan_name)
3626 {
3627  RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup);
3628  struct confbridge_user *user;
3629  int all = !strcasecmp("all", chan_name);
3630  int participants = !strcasecmp("participants", chan_name);
3631  int res = -2;
3632 
3633  conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
3634  if (!conference) {
3635  return -1;
3636  }
3637 
3638  {
3639  SCOPED_AO2LOCK(bridge_lock, conference);
3640  AST_LIST_TRAVERSE(&conference->active_list, user, list) {
3641  int match = !strncasecmp(chan_name, ast_channel_name(user->chan),
3642  strlen(chan_name));
3643  if (match || all
3644  || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
3645  generic_mute_unmute_user(conference, user, mute);
3646  res = 0;
3647  if (match) {
3648  return res;
3649  }
3650  }
3651  }
3652 
3653  AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
3654  int match = !strncasecmp(chan_name, ast_channel_name(user->chan),
3655  strlen(chan_name));
3656  if (match || all
3657  || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
3658  generic_mute_unmute_user(conference, user, mute);
3659  res = 0;
3660  if (match) {
3661  return res;
3662  }
3663  }
3664  }
3665  }
3666 
3667  return res;
3668 }
3669 
3670 static int cli_mute_unmute_helper(int mute, struct ast_cli_args *a)
3671 {
3672  int res = generic_mute_unmute_helper(mute, a->argv[2], a->argv[3]);
3673 
3674  if (res == -1) {
3675  ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
3676  return -1;
3677  } else if (res == -2) {
3678  if (!strcasecmp("all", a->argv[3]) || !strcasecmp("participants", a->argv[3])) {
3679  ast_cli(a->fd, "No participants found in conference %s\n", a->argv[2]);
3680  } else {
3681  ast_cli(a->fd, "No channel named '%s' found in conference %s\n", a->argv[3], a->argv[2]);
3682  }
3683  return -1;
3684  }
3685  ast_cli(a->fd, "%s %s from confbridge %s\n", mute ? "Muting" : "Unmuting", a->argv[3], a->argv[2]);
3686  return 0;
3687 }
3688 
3689 static char *handle_cli_confbridge_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3690 {
3691  switch (cmd) {
3692  case CLI_INIT:
3693  e->command = "confbridge mute";
3694  e->usage =
3695  "Usage: confbridge mute <conference> <channel>\n"
3696  " Mute a channel in a conference.\n"
3697  " (all to mute everyone, participants to mute non-admins)\n"
3698  " If the specified channel is a prefix,\n"
3699  " the action will be taken on the first\n"
3700  " matching channel.\n";
3701  return NULL;
3702  case CLI_GENERATE:
3703  if (a->pos == 2) {
3704  return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3705  }
3706  if (a->pos == 3) {
3707  return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
3708  }
3709  return NULL;
3710  }
3711  if (a->argc != 4) {
3712  return CLI_SHOWUSAGE;
3713  }
3714 
3715  cli_mute_unmute_helper(1, a);
3716 
3717  return CLI_SUCCESS;
3718 }
3719 
3720 static char *handle_cli_confbridge_unmute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3721 {
3722  switch (cmd) {
3723  case CLI_INIT:
3724  e->command = "confbridge unmute";
3725  e->usage =
3726  "Usage: confbridge unmute <conference> <channel>\n"
3727  " Unmute a channel in a conference.\n"
3728  " (all to unmute everyone, participants to unmute non-admins)\n"
3729  " If the specified channel is a prefix,\n"
3730  " the action will be taken on the first\n"
3731  " matching channel.\n";
3732  return NULL;
3733  case CLI_GENERATE:
3734  if (a->pos == 2) {
3735  return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3736  }
3737  if (a->pos == 3) {
3738  return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
3739  }
3740  return NULL;
3741  }
3742  if (a->argc != 4) {
3743  return CLI_SHOWUSAGE;
3744  }
3745 
3746  cli_mute_unmute_helper(0, a);
3747 
3748  return CLI_SUCCESS;
3749 }
3750 
3751 static char *handle_cli_confbridge_lock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3752 {
3753  switch (cmd) {
3754  case CLI_INIT:
3755  e->command = "confbridge lock";
3756  e->usage =
3757  "Usage: confbridge lock <conference>\n"
3758  " Lock a conference. While locked, no new non-admins\n"
3759  " may join the conference.\n";
3760  return NULL;
3761  case CLI_GENERATE:
3762  if (a->pos == 2) {
3763  return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3764  }
3765  return NULL;
3766  }
3767  if (a->argc != 3) {
3768  return CLI_SHOWUSAGE;
3769  }
3770  if (generic_lock_unlock_helper(1, a->argv[2])) {
3771  ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
3772  } else {
3773  ast_cli(a->fd, "Conference %s is locked.\n", a->argv[2]);
3774  }
3775  return CLI_SUCCESS;
3776 }
3777 
3778 static char *handle_cli_confbridge_unlock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3779 {
3780  switch (cmd) {
3781  case CLI_INIT:
3782  e->command = "confbridge unlock";
3783  e->usage =
3784  "Usage: confbridge unlock <conference>\n"
3785  " Unlock a previously locked conference.\n";
3786  return NULL;
3787  case CLI_GENERATE:
3788  if (a->pos == 2) {
3789  return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3790  }
3791  return NULL;
3792  }
3793  if (a->argc != 3) {
3794  return CLI_SHOWUSAGE;
3795  }
3796  if (generic_lock_unlock_helper(0, a->argv[2])) {
3797  ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
3798  } else {
3799  ast_cli(a->fd, "Conference %s is unlocked.\n", a->argv[2]);
3800  }
3801  return CLI_SUCCESS;
3802 }
3803 
3804 static char *handle_cli_confbridge_start_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3805 {
3806  const char *rec_file = NULL;
3807  struct confbridge_conference *conference;
3808 
3809  switch (cmd) {
3810  case CLI_INIT:
3811  e->command = "confbridge record start";
3812  e->usage =
3813  "Usage: confbridge record start <conference> <file>\n"
3814  " <file> is optional, Otherwise the bridge profile\n"
3815  " record file will be used. If the bridge profile\n"
3816  " has no record file specified, a file will automatically\n"
3817  " be generated in the monitor directory\n";
3818  return NULL;
3819  case CLI_GENERATE:
3820  if (a->pos == 3) {
3821  return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3822  }
3823  return NULL;
3824  }
3825  if (a->argc < 4) {
3826  return CLI_SHOWUSAGE;
3827  }
3828  if (a->argc == 5) {
3829  rec_file = a->argv[4];
3830  }
3831 
3832  conference = ao2_find(conference_bridges, a->argv[3], OBJ_KEY);
3833  if (!conference) {
3834  ast_cli(a->fd, "Conference not found.\n");
3835  return CLI_FAILURE;
3836  }
3837  ao2_lock(conference);
3838  if (conf_is_recording(conference)) {
3839  ast_cli(a->fd, "Conference is already being recorded.\n");
3840  ao2_unlock(conference);
3841  ao2_ref(conference, -1);
3842  return CLI_SUCCESS;
3843  }
3844  if (!ast_strlen_zero(rec_file)) {
3845  ast_copy_string(conference->b_profile.rec_file, rec_file, sizeof(conference->b_profile.rec_file));
3846  }
3847 
3848  if (conf_start_record(conference)) {
3849  ast_cli(a->fd, "Could not start recording due to internal error.\n");
3850  ao2_unlock(conference);
3851  ao2_ref(conference, -1);
3852  return CLI_FAILURE;
3853  }
3854  ao2_unlock(conference);
3855 
3856  ast_cli(a->fd, "Recording started\n");
3857  ao2_ref(conference, -1);
3858  return CLI_SUCCESS;
3859 }
3860 
3861 static char *handle_cli_confbridge_stop_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3862 {
3863  struct confbridge_conference *conference;
3864  int ret;
3865 
3866  switch (cmd) {
3867  case CLI_INIT:
3868  e->command = "confbridge record stop";
3869  e->usage =
3870  "Usage: confbridge record stop <conference>\n"
3871  " Stop a previously started recording.\n";
3872  return NULL;
3873  case CLI_GENERATE:
3874  if (a->pos == 3) {
3875  return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3876  }
3877  return NULL;
3878  }
3879  if (a->argc != 4) {
3880  return CLI_SHOWUSAGE;
3881  }
3882 
3883  conference = ao2_find(conference_bridges, a->argv[3], OBJ_KEY);
3884  if (!conference) {
3885  ast_cli(a->fd, "Conference not found.\n");
3886  return CLI_SUCCESS;
3887  }
3888  ao2_lock(conference);
3889  ret = conf_stop_record(conference);
3890  ao2_unlock(conference);
3891  ast_cli(a->fd, "Recording %sstopped.\n", ret ? "could not be " : "");
3892  ao2_ref(conference, -1);
3893  return CLI_SUCCESS;
3894 }
3895 
3896 static struct ast_cli_entry cli_confbridge[] = {
3897  AST_CLI_DEFINE(handle_cli_confbridge_list, "List conference bridges and participants."),
3898  AST_CLI_DEFINE(handle_cli_confbridge_kick, "Kick participants out of conference bridges."),
3899  AST_CLI_DEFINE(handle_cli_confbridge_mute, "Mute participants."),
3900  AST_CLI_DEFINE(handle_cli_confbridge_unmute, "Unmute participants."),
3901  AST_CLI_DEFINE(handle_cli_confbridge_lock, "Lock a conference."),
3902  AST_CLI_DEFINE(handle_cli_confbridge_unlock, "Unlock a conference."),
3903  AST_CLI_DEFINE(handle_cli_confbridge_start_record, "Start recording a conference"),
3904  AST_CLI_DEFINE(handle_cli_confbridge_stop_record, "Stop recording a conference."),
3905 };
3906 static struct ast_custom_function confbridge_function = {
3907  .name = "CONFBRIDGE",
3908  .write = func_confbridge_helper,
3909 };
3910 
3911 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
3912 static struct ast_custom_function confbridge_info_function = {
3913  .name = "CONFBRIDGE_INFO",
3914  .read = func_confbridge_info,
3915 };
3916 
3917 static int func_confbridge_channels(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
3918 {
3919  char *parse, *outbuf;
3920  struct confbridge_conference *conference;
3921  struct confbridge_user *user;
3922  int bytes, count = 0;
3923  size_t outlen;
3924  AST_DECLARE_APP_ARGS(args,
3925  AST_APP_ARG(type);
3926  AST_APP_ARG(confno);
3927  );
3928 
3929  /* parse all the required arguments and make sure they exist. */
3930  if (ast_strlen_zero(data)) {
3931  return -1;
3932  }
3933  parse = ast_strdupa(data);
3934  AST_STANDARD_APP_ARGS(args, parse);
3935  if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
3936  ast_log(LOG_WARNING, "Usage: %s(category,confno)", cmd);
3937  return -1;
3938  }
3939  conference = ao2_find(conference_bridges, args.confno, OBJ_KEY);
3940  if (!conference) {
3941  ast_debug(1, "No such conference: %s\n", args.confno);
3942  return -1;
3943  }
3944 
3945  outbuf = buf;
3946  outlen = len;
3947 
3948  ao2_lock(conference);
3949  if (!strcasecmp(args.type, "parties")) {
3950  AST_LIST_TRAVERSE(&conference->active_list, user, list) {
3951  bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
3952  outbuf += bytes;
3953  outlen -= bytes;
3954  }
3955  AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
3956  bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
3957  outbuf += bytes;
3958  outlen -= bytes;
3959  }
3960  } else if (!strcasecmp(args.type, "active")) {
3961  AST_LIST_TRAVERSE(&conference->active_list, user, list) {
3962  bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
3963  outbuf += bytes;
3964  outlen -= bytes;
3965  }
3966  } else if (!strcasecmp(args.type, "waiting")) {
3967  AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
3968  bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
3969  outbuf += bytes;
3970  outlen -= bytes;
3971  }
3972  } else if (!strcasecmp(args.type, "admins")) {
3973  AST_LIST_TRAVERSE(&conference->active_list, user, list) {
3974  if (ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
3975  bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
3976  outbuf += bytes;
3977  outlen -= bytes;
3978  }
3979  }
3980  } else if (!strcasecmp(args.type, "marked")) {
3981  AST_LIST_TRAVERSE(&conference->active_list, user, list) {
3982  if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
3983  bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
3984  outbuf += bytes;
3985  outlen -= bytes;
3986  }
3987  }
3988  } else {
3989  ast_log(LOG_ERROR, "Invalid keyword '%s' passed to %s.\n", args.type, cmd);
3990  }
3991  ao2_unlock(conference);
3992  ao2_ref(conference, -1);
3993  return 0;
3994 }
3995 
3996 static struct ast_custom_function confbridge_channels_function = {
3997  .name = "CONFBRIDGE_CHANNELS",
3998  .read = func_confbridge_channels,
3999 };
4000 
4001 static int action_confbridgelist_item(struct mansession *s, const char *id_text, struct confbridge_conference *conference, struct confbridge_user *user, int waiting)
4002 {
4003  struct ast_channel_snapshot *snapshot;
4004  struct ast_str *snap_str;
4005 
4006  snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(user->chan));
4007  if (!snapshot) {
4008  return 0;
4009  }
4010 
4011  snap_str = ast_manager_build_channel_state_string(snapshot);
4012  if (!snap_str) {
4013  ao2_ref(snapshot, -1);
4014  return 0;
4015  }
4016 
4017  astman_append(s,
4018  "Event: ConfbridgeList\r\n"
4019  "%s"
4020  "Conference: %s\r\n"
4021  "Admin: %s\r\n"
4022  "MarkedUser: %s\r\n"
4023  "WaitMarked: %s\r\n"
4024  "EndMarked: %s\r\n"
4025  "EndMarkedAny: %s\r\n"
4026  "Waiting: %s\r\n"
4027  "Muted: %s\r\n"
4028  "Talking: %s\r\n"
4029  "AnsweredTime: %d\r\n"
4030  "%s"
4031  "\r\n",
4032  id_text,
4033  conference->name,
4034  AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_ADMIN)),
4035  AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)),
4036  AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)),
4037  AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_ENDMARKED)),
4038  AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_ENDMARKEDANY)),
4039  AST_YESNO(waiting),
4040  AST_YESNO(user->muted),
4041  AST_YESNO(user->talking),
4043  ast_str_buffer(snap_str));
4044 
4045  ast_free(snap_str);
4046  ao2_ref(snapshot, -1);
4047 
4048  return 1;
4049 }
4050 
4051 static int action_confbridgelist(struct mansession *s, const struct message *m)
4052 {
4053  const char *actionid = astman_get_header(m, "ActionID");
4054  const char *conference_name = astman_get_header(m, "Conference");
4055  struct confbridge_user *user;
4056  struct confbridge_conference *conference;
4057  char id_text[80];
4058  int total = 0;
4059 
4060  id_text[0] = '\0';
4061  if (!ast_strlen_zero(actionid)) {
4062  snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
4063  }
4064  if (ast_strlen_zero(conference_name)) {
4065  astman_send_error(s, m, "No Conference name provided.");
4066  return 0;
4067  }
4068  if (!ao2_container_count(conference_bridges)) {
4069  astman_send_error(s, m, "No active conferences.");
4070  return 0;
4071  }
4072  conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
4073  if (!conference) {
4074  astman_send_error(s, m, "No Conference by that name found.");
4075  return 0;
4076  }
4077 
4078  astman_send_listack(s, m, "Confbridge user list will follow", "start");
4079 
4080  ao2_lock(conference);
4081  AST_LIST_TRAVERSE(&conference->active_list, user, list) {
4082  total += action_confbridgelist_item(s, id_text, conference, user, 0);
4083  }
4084  AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
4085  total += action_confbridgelist_item(s, id_text, conference, user, 1);
4086  }
4087  ao2_unlock(conference);
4088  ao2_ref(conference, -1);
4089 
4090  astman_send_list_complete_start(s, m, "ConfbridgeListComplete", total);
4092 
4093  return 0;
4094 }
4095 
4096 static int action_confbridgelistrooms(struct mansession *s, const struct message *m)
4097 {
4098  const char *actionid = astman_get_header(m, "ActionID");
4099  struct confbridge_conference *conference;
4100  struct ao2_iterator iter;
4101  char id_text[512] = "";
4102  int totalitems = 0;
4103 
4104  if (!ast_strlen_zero(actionid)) {
4105  snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
4106  }
4107 
4108  if (!ao2_container_count(conference_bridges)) {
4109  astman_send_error(s, m, "No active conferences.");
4110  return 0;
4111  }
4112 
4113  astman_send_listack(s, m, "Confbridge conferences will follow", "start");
4114 
4115  /* Traverse the conference list */
4116  iter = ao2_iterator_init(conference_bridges, 0);
4117  while ((conference = ao2_iterator_next(&iter))) {
4118  totalitems++;
4119 
4120  ao2_lock(conference);
4121  astman_append(s,
4122  "Event: ConfbridgeListRooms\r\n"
4123  "%s"
4124  "Conference: %s\r\n"
4125  "Parties: %u\r\n"
4126  "Marked: %u\r\n"
4127  "Locked: %s\r\n"
4128  "Muted: %s\r\n"
4129  "\r\n",
4130  id_text,
4131  conference->name,
4132  conference->activeusers + conference->waitingusers,
4133  conference->markedusers,
4134  AST_YESNO(conference->locked),
4135  AST_YESNO(conference->muted));
4136  ao2_unlock(conference);
4137 
4138  ao2_ref(conference, -1);
4139  }
4140  ao2_iterator_destroy(&iter);
4141 
4142  /* Send final confirmation */
4143  astman_send_list_complete_start(s, m, "ConfbridgeListRoomsComplete", totalitems);
4145  return 0;
4146 }
4147 
4148 static int action_mute_unmute_helper(struct mansession *s, const struct message *m, int mute)
4149 {
4150  const char *conference_name = astman_get_header(m, "Conference");
4151  const char *channel_name = astman_get_header(m, "Channel");
4152  int res = 0;
4153 
4154  if (ast_strlen_zero(conference_name)) {
4155  astman_send_error(s, m, "No Conference name provided.");
4156  return 0;
4157  }
4158  if (ast_strlen_zero(channel_name)) {
4159  astman_send_error(s, m, "No channel name provided.");
4160  return 0;
4161  }
4162  if (!ao2_container_count(conference_bridges)) {
4163  astman_send_error(s, m, "No active conferences.");
4164  return 0;
4165  }
4166 
4167  res = generic_mute_unmute_helper(mute, conference_name, channel_name);
4168 
4169  if (res == -1) {
4170  astman_send_error(s, m, "No Conference by that name found.");
4171  return 0;
4172  } else if (res == -2) {
4173  astman_send_error(s, m, "No Channel by that name found in Conference.");
4174  return 0;
4175  }
4176 
4177  astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
4178  return 0;
4179 }
4180 
4181 static int action_confbridgeunmute(struct mansession *s, const struct message *m)
4182 {
4183  return action_mute_unmute_helper(s, m, 0);
4184 }
4185 static int action_confbridgemute(struct mansession *s, const struct message *m)
4186 {
4187  return action_mute_unmute_helper(s, m, 1);
4188 }
4189 
4190 static int action_lock_unlock_helper(struct mansession *s, const struct message *m, int lock)
4191 {
4192  const char *conference_name = astman_get_header(m, "Conference");
4193  int res = 0;
4194 
4195  if (ast_strlen_zero(conference_name)) {
4196  astman_send_error(s, m, "No Conference name provided.");
4197  return 0;
4198  }
4199  if (!ao2_container_count(conference_bridges)) {
4200  astman_send_error(s, m, "No active conferences.");
4201  return 0;
4202  }
4203  if ((res = generic_lock_unlock_helper(lock, conference_name))) {
4204  astman_send_error(s, m, "No Conference by that name found.");
4205  return 0;
4206  }
4207  astman_send_ack(s, m, lock ? "Conference locked" : "Conference unlocked");
4208  return 0;
4209 }
4210 static int action_confbridgeunlock(struct mansession *s, const struct message *m)
4211 {
4212  return action_lock_unlock_helper(s, m, 0);
4213 }
4214 static int action_confbridgelock(struct mansession *s, const struct message *m)
4215 {
4216  return action_lock_unlock_helper(s, m, 1);
4217 }
4218 
4219 static int action_confbridgekick(struct mansession *s, const struct message *m)
4220 {
4221  const char *conference_name = astman_get_header(m, "Conference");
4222  const char *channel = astman_get_header(m, "Channel");
4223  struct confbridge_conference *conference;
4224  int found;
4225 
4226  if (ast_strlen_zero(conference_name)) {
4227  astman_send_error(s, m, "No Conference name provided.");
4228  return 0;
4229  }
4230  if (!ao2_container_count(conference_bridges)) {
4231  astman_send_error(s, m, "No active conferences.");
4232  return 0;
4233  }
4234 
4235  conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
4236  if (!conference) {
4237  astman_send_error(s, m, "No Conference by that name found.");
4238  return 0;
4239  }
4240 
4241  found = !kick_conference_participant(conference, channel);
4242  ao2_ref(conference, -1);
4243 
4244  if (found) {
4245  astman_send_ack(s, m, !strcmp("all", channel) ? "All participants kicked" : "User kicked");
4246  } else {
4247  astman_send_error(s, m, "No Channel by that name found in Conference.");
4248  }
4249  return 0;
4250 }
4251 
4252 static int action_confbridgestartrecord(struct mansession *s, const struct message *m)
4253 {
4254  const char *conference_name = astman_get_header(m, "Conference");
4255  const char *recordfile = astman_get_header(m, "RecordFile");
4256  struct confbridge_conference *conference;
4257 
4258  if (ast_strlen_zero(conference_name)) {
4259  astman_send_error(s, m, "No Conference name provided.");
4260  return 0;
4261  }
4262  if (!ao2_container_count(conference_bridges)) {
4263  astman_send_error(s, m, "No active conferences.");
4264  return 0;
4265  }
4266 
4267  conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
4268  if (!conference) {
4269  astman_send_error(s, m, "No Conference by that name found.");
4270  return 0;
4271  }
4272 
4273  ao2_lock(conference);
4274  if (conf_is_recording(conference)) {
4275  astman_send_error(s, m, "Conference is already being recorded.");
4276  ao2_unlock(conference);
4277  ao2_ref(conference, -1);
4278  return 0;
4279  }
4280 
4281  if (!ast_strlen_zero(recordfile)) {
4282  ast_copy_string(conference->b_profile.rec_file, recordfile, sizeof(conference->b_profile.rec_file));
4283  }
4284 
4285  if (conf_start_record(conference)) {
4286  astman_send_error(s, m, "Internal error starting conference recording.");
4287  ao2_unlock(conference);
4288  ao2_ref(conference, -1);
4289  return 0;
4290  }
4291  ao2_unlock(conference);
4292 
4293  ao2_ref(conference, -1);
4294  astman_send_ack(s, m, "Conference Recording Started.");
4295  return 0;
4296 }
4297 static int action_confbridgestoprecord(struct mansession *s, const struct message *m)
4298 {
4299  const char *conference_name = astman_get_header(m, "Conference");
4300  struct confbridge_conference *conference;
4301 
4302  if (ast_strlen_zero(conference_name)) {
4303  astman_send_error(s, m, "No Conference name provided.");
4304  return 0;
4305  }
4306  if (!ao2_container_count(conference_bridges)) {
4307  astman_send_error(s, m, "No active conferences.");
4308  return 0;
4309  }
4310 
4311  conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
4312  if (!conference) {
4313  astman_send_error(s, m, "No Conference by that name found.");
4314  return 0;
4315  }
4316 
4317  ao2_lock(conference);
4318  if (conf_stop_record(conference)) {
4319  ao2_unlock(conference);
4320  astman_send_error(s, m, "Internal error while stopping recording.");
4321  ao2_ref(conference, -1);
4322  return 0;
4323  }
4324  ao2_unlock(conference);
4325 
4326  ao2_ref(conference, -1);
4327  astman_send_ack(s, m, "Conference Recording Stopped.");
4328  return 0;
4329 }
4330 
4331 static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m)
4332 {
4333  const char *conference_name = astman_get_header(m, "Conference");
4334  const char *channel = astman_get_header(m, "Channel");
4335  struct confbridge_user *user;
4336  struct confbridge_conference *conference;
4337 
4338  if (ast_strlen_zero(conference_name)) {
4339  astman_send_error(s, m, "No Conference name provided.");
4340  return 0;
4341  }
4342  if (ast_strlen_zero(channel)) {
4343  astman_send_error(s, m, "No channel name provided.");
4344  return 0;
4345  }
4346  if (!ao2_container_count(conference_bridges)) {
4347  astman_send_error(s, m, "No active conferences.");
4348  return 0;
4349  }
4350 
4351  conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
4352  if (!conference) {
4353  astman_send_error(s, m, "No Conference by that name found.");
4354  return 0;
4355  }
4356 
4357  /* find channel and set as video src. */
4358  ao2_lock(conference);
4359  AST_LIST_TRAVERSE(&conference->active_list, user, list) {
4360  if (!strncmp(channel, ast_channel_name(user->chan), strlen(channel))) {
4361  ast_bridge_set_single_src_video_mode(conference->bridge, user->chan);
4362  break;
4363  }
4364  }
4365  ao2_unlock(conference);
4366  ao2_ref(conference, -1);
4367 
4368  /* do not access user after conference unlock. We are just
4369  * using this check to see if it was found or not */
4370  if (!user) {
4371  astman_send_error(s, m, "No channel by that name found in conference.");
4372  return 0;
4373  }
4374  astman_send_ack(s, m, "Conference single video source set.");
4375  return 0;
4376 }
4377 
4378 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4379 {
4380  char *parse;
4381  struct confbridge_conference *conference;
4382  struct confbridge_user *user;
4383  int count = 0;
4384  AST_DECLARE_APP_ARGS(args,
4385  AST_APP_ARG(type);
4386  AST_APP_ARG(confno);
4387  );
4388 
4389  /* parse all the required arguments and make sure they exist. */
4390  if (ast_strlen_zero(data)) {
4391  return -1;
4392  }
4393  parse = ast_strdupa(data);
4394  AST_STANDARD_APP_ARGS(args, parse);
4395  if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
4396  return -1;
4397  }
4398  conference = ao2_find(conference_bridges, args.confno, OBJ_KEY);
4399  if (!conference) {
4400  snprintf(buf, len, "0");
4401  return 0;
4402  }
4403 
4404  /* get the correct count for the type requested */
4405  ao2_lock(conference);
4406  if (!strcasecmp(args.type, "parties")) {
4407  AST_LIST_TRAVERSE(&conference->active_list, user, list) {
4408  count++;
4409  }
4410  AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
4411  count++;
4412  }
4413  } else if (!strcasecmp(args.type, "admins")) {
4414  AST_LIST_TRAVERSE(&conference->active_list, user, list) {
4415  if (ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
4416  count++;
4417  }
4418  }
4419  } else if (!strcasecmp(args.type, "marked")) {
4420  AST_LIST_TRAVERSE(&conference->active_list, user, list) {
4421  if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
4422  count++;
4423  }
4424  }
4425  } else if (!strcasecmp(args.type, "locked")) {
4426  count = conference->locked;
4427  } else if (!strcasecmp(args.type, "muted")) {
4428  count = conference->muted;
4429  } else {
4430  ast_log(LOG_ERROR, "Invalid keyword '%s' passed to CONFBRIDGE_INFO.\n", args.type);
4431  }
4432  snprintf(buf, len, "%d", count);
4433  ao2_unlock(conference);
4434  ao2_ref(conference, -1);
4435  return 0;
4436 }
4437 
4438 static int confkick_exec(struct ast_channel *chan, const char *data)
4439 {
4440  char *parse;
4441  struct confbridge_conference *conference;
4442  int not_found;
4443 
4444  AST_DECLARE_APP_ARGS(args,
4445  AST_APP_ARG(confbridge);
4446  AST_APP_ARG(channel);
4447  );
4448 
4449  if (ast_strlen_zero(data)) {
4450  ast_log(LOG_WARNING, "No conference bridge specified.\n");
4451  pbx_builtin_setvar_helper(chan, "CONFKICKSTATUS", "FAILURE");
4452  return 0;
4453  }
4454 
4455  parse = ast_strdupa(data);
4456  AST_STANDARD_APP_ARGS(args, parse);
4457 
4458  conference = ao2_find(conference_bridges, args.confbridge, OBJ_KEY);
4459  if (!conference) {
4460  ast_log(LOG_WARNING, "No conference bridge named '%s' found!\n", args.confbridge);
4461  pbx_builtin_setvar_helper(chan, "CONFKICKSTATUS", "FAILURE");
4462  return 0;
4463  }
4464  if (ast_strlen_zero(args.channel)) {
4465  not_found = kick_conference_participant(conference, "all");
4466  } else {
4467  not_found = kick_conference_participant(conference, args.channel);
4468  }
4469 
4470  ao2_ref(conference, -1);
4471  if (not_found) {
4472  if (ast_strlen_zero(args.channel) || !strcasecmp("all", args.channel) || !strcasecmp("participants", args.channel)) {
4473  ast_log(LOG_WARNING, "No participants found in conference bridge '%s'!\n", args.confbridge);
4474  } else {
4475  ast_log(LOG_WARNING, "No participant named '%s' found in conference bridge '%s'!\n", args.channel, args.confbridge);
4476  }
4477  pbx_builtin_setvar_helper(chan, "CONFKICKSTATUS", "FAILURE");
4478  return 0;
4479  }
4480  ast_debug(1, "Kicked '%s' out of conference '%s'\n", args.channel, args.confbridge);
4481  pbx_builtin_setvar_helper(chan, "CONFKICKSTATUS", "SUCCESS");
4482  return 0;
4483 }
4484 
4485 void conf_add_user_active(struct confbridge_conference *conference, struct confbridge_user *user)
4486 {
4487  AST_LIST_INSERT_TAIL(&conference->active_list, user, list);
4488  conference->activeusers++;
4489 }
4490 
4491 void conf_add_user_marked(struct confbridge_conference *conference, struct confbridge_user *user)
4492 {
4493  AST_LIST_INSERT_TAIL(&conference->active_list, user, list);
4494  conference->activeusers++;
4495  conference->markedusers++;
4496 }
4497 
4498 void conf_add_user_waiting(struct confbridge_conference *conference, struct confbridge_user *user)
4499 {
4500  AST_LIST_INSERT_TAIL(&conference->waiting_list, user, list);
4501  conference->waitingusers++;
4502 }
4503 
4504 void conf_remove_user_active(struct confbridge_conference *conference, struct confbridge_user *user)
4505 {
4506  AST_LIST_REMOVE(&conference->active_list, user, list);
4507  conference->activeusers--;
4508 }
4509 
4510 void conf_remove_user_marked(struct confbridge_conference *conference, struct confbridge_user *user)
4511 {
4512  AST_LIST_REMOVE(&conference->active_list, user, list);
4513  conference->activeusers--;
4514  conference->markedusers--;
4515 }
4516 
4518 {
4519  struct confbridge_user *only_user = AST_LIST_FIRST(&conference->active_list);
4520 
4521  /* Turn on MOH if the single participant is set up for it */
4522  if (ast_test_flag(&only_user->u_profile, USER_OPT_MUSICONHOLD)) {
4523  conf_moh_start(only_user);
4524  }
4525  conf_update_user_mute(only_user);
4526 }
4527 
4529 {
4530  AST_LIST_REMOVE(&conference->waiting_list, user, list);
4531  conference->waitingusers--;
4532 }
4533 
4534 /*!
4535  * \internal
4536  * \brief Unregister a ConfBridge channel technology.
4537  * \since 12.0.0
4538  *
4539  * \param tech What to unregister.
4540  */
4541 static void unregister_channel_tech(struct ast_channel_tech *tech)
4542 {
4543  ast_channel_unregister(tech);
4544  ao2_cleanup(tech->capabilities);
4545 }
4546 
4547 /*!
4548  * \internal
4549  * \brief Register a ConfBridge channel technology.
4550  * \since 12.0.0
4551  *
4552  * \param tech What to register.
4553  *
4554  * \retval 0 on success.
4555  * \retval -1 on error.
4556  */
4557 static int register_channel_tech(struct ast_channel_tech *tech)
4558 {
4560  if (!tech->capabilities) {
4561  return -1;
4562  }
4563  ast_format_cap_append_by_type(tech->capabilities, AST_MEDIA_TYPE_UNKNOWN);
4564  if (ast_channel_register(tech)) {
4565  ast_log(LOG_ERROR, "Unable to register channel technology %s(%s).\n",
4566  tech->type, tech->description);
4567  return -1;
4568  }
4569  return 0;
4570 }
4571 
4572 /*! \brief Called when module is being unloaded */
4573 static int unload_module(void)
4574 {
4577 
4578  ast_custom_function_unregister(&confbridge_function);
4579  ast_custom_function_unregister(&confbridge_info_function);
4580  ast_custom_function_unregister(&confbridge_channels_function);
4581 
4582  ast_cli_unregister_multiple(cli_confbridge, ARRAY_LEN(cli_confbridge));
4583 
4584  ast_manager_unregister("ConfbridgeList");
4585  ast_manager_unregister("ConfbridgeListRooms");
4586  ast_manager_unregister("ConfbridgeMute");
4587  ast_manager_unregister("ConfbridgeUnmute");
4588  ast_manager_unregister("ConfbridgeKick");
4589  ast_manager_unregister("ConfbridgeUnlock");
4590  ast_manager_unregister("ConfbridgeLock");
4591  ast_manager_unregister("ConfbridgeStartRecord");
4592  ast_manager_unregister("ConfbridgeStopRecord");
4593  ast_manager_unregister("ConfbridgeSetSingleVideoSrc");
4594 
4595  /* Unsubscribe from stasis confbridge message type and clean it up. */
4597 
4598  /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
4599  ao2_cleanup(conference_bridges);
4600  conference_bridges = NULL;
4601 
4603 
4604  unregister_channel_tech(conf_announce_get_tech());
4605  unregister_channel_tech(conf_record_get_tech());
4606 
4607  return 0;
4608 }
4609 
4610 /*!
4611  * \brief Load the module
4612  *
4613  * Module loading including tests for configuration or dependencies.
4614  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
4615  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
4616  * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
4617  * configuration file or other non-critical problem return
4618  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
4619  */
4620 static int load_module(void)
4621 {
4622  int res = 0;
4623 
4624  if (conf_load_config()) {
4625  ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
4626  return AST_MODULE_LOAD_DECLINE;
4627  }
4628 
4629  if (register_channel_tech(conf_record_get_tech())
4630  || register_channel_tech(conf_announce_get_tech())) {
4631  unload_module();
4632  return AST_MODULE_LOAD_DECLINE;
4633  }
4634 
4635  /* Create a container to hold the conference bridges */
4636  conference_bridges = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
4639  if (!conference_bridges) {
4640  unload_module();
4641  return AST_MODULE_LOAD_DECLINE;
4642  }
4643 
4644  /* Setup manager stasis subscriptions */
4645  res |= manager_confbridge_init();
4646 
4648  res |= ast_register_application_xml(app2, confkick_exec);
4649 
4650  res |= ast_custom_function_register_escalating(&confbridge_function, AST_CFE_WRITE);
4651  res |= ast_custom_function_register(&confbridge_info_function);
4652  res |= ast_custom_function_register(&confbridge_channels_function);
4653 
4654  res |= ast_cli_register_multiple(cli_confbridge, ARRAY_LEN(cli_confbridge));
4655 
4656  res |= ast_manager_register_xml("ConfbridgeList", EVENT_FLAG_REPORTING, action_confbridgelist);
4657  res |= ast_manager_register_xml("ConfbridgeListRooms", EVENT_FLAG_REPORTING, action_confbridgelistrooms);
4658  res |= ast_manager_register_xml("ConfbridgeMute", EVENT_FLAG_CALL, action_confbridgemute);
4659  res |= ast_manager_register_xml("ConfbridgeUnmute", EVENT_FLAG_CALL, action_confbridgeunmute);
4660  res |= ast_manager_register_xml("ConfbridgeKick", EVENT_FLAG_CALL, action_confbridgekick);
4661  res |= ast_manager_register_xml("ConfbridgeUnlock", EVENT_FLAG_CALL, action_confbridgeunlock);
4662  res |= ast_manager_register_xml("ConfbridgeLock", EVENT_FLAG_CALL, action_confbridgelock);
4663  res |= ast_manager_register_xml("ConfbridgeStartRecord", EVENT_FLAG_SYSTEM, action_confbridgestartrecord);
4664  res |= ast_manager_register_xml("ConfbridgeStopRecord", EVENT_FLAG_SYSTEM, action_confbridgestoprecord);
4665  res |= ast_manager_register_xml("ConfbridgeSetSingleVideoSrc", EVENT_FLAG_CALL, action_confbridgesetsinglevideosrc);
4666  if (res) {
4667  unload_module();
4668  return AST_MODULE_LOAD_DECLINE;
4669  }
4670 
4671  return AST_MODULE_LOAD_SUCCESS;
4672 }
4673 
4674 static int reload(void)
4675 {
4676  return conf_reload_config();
4677 }
4678 
4679 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Conference Bridge Application",
4680  .support_level = AST_MODULE_SUPPORT_CORE,
4681  .load = load_module,
4682  .unload = unload_module,
4683  .reload = reload,
4684  .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
4685  .optional_modules = "codec_speex,func_jitterbuffer",
4686 );
const char * name
Definition: pbx.h:119
const char * type
Definition: datastore.h:32
void conf_remove_user_marked(struct confbridge_conference *conference, struct confbridge_user *user)
Remove a conference bridge user from the marked active conference users in the conference.
int ast_audiohook_volume_set(struct ast_channel *chan, enum ast_audiohook_direction direction, int volume)
Adjust the volume on frames read from or written to a channel.
Definition: audiohook.c:1293
void ast_bridge_set_sfu_video_mode(struct ast_bridge *bridge)
Set the bridge to be a selective forwarding unit.
Definition: bridge.c:3774
const ast_string_field data
void conf_add_user_active(struct confbridge_conference *conference, struct confbridge_user *user)
Add a conference bridge user as an unmarked active user of the conference.
Options for ast_pbx_run()
Definition: pbx.h:407
Main Channel structure associated with a channel.
int ast_bridge_remove(struct ast_bridge *bridge, struct ast_channel *chan)
Remove a channel from a bridge.
Definition: bridge.c:1951
static int announce_user_count(struct confbridge_conference *conference, struct confbridge_user *user, struct ast_bridge_channel *bridge_channel)
Announce number of users in the conference bridge to the caller.
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
void ast_bridge_features_cleanup(struct ast_bridge_features *features)
Clean up the contents of a bridge features structure.
Definition: bridge.c:3653
Music on hold handling.
conference_event_fn join_unmarked
Definition: conf_state.h:47
int conf_add_post_join_action(struct confbridge_user *user, int(*func)(struct confbridge_user *user))
Queue a function to run with the given conference bridge user as an argument once the state transitio...
char * str
Subscriber phone number (Malloced)
Definition: channel.h:291
void ast_bridge_set_mixing_interval(struct ast_bridge *bridge, unsigned int mixing_interval)
Adjust the internal mixing interval of a bridge used during multimix mode.
Definition: bridge.c:3698
void ast_taskprocessor_build_name(char *buf, unsigned int size, const char *format,...)
Build a taskprocessor name with a sequence number on the end.
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1293
struct ast_channel_snapshot_base * base
Asterisk locking-related definitions:
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:3310
Asterisk main include file. File version handling, generic pbx functions.
int ast_shutting_down(void)
Definition: asterisk.c:1876
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:421
void conf_remove_user_active(struct confbridge_conference *conference, struct confbridge_user *user)
Remove a conference bridge user from the unmarked active conference users in the conference.
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
int manager_confbridge_init(void)
register stasis message routers to handle manager events for confbridge messages
void ast_bridge_set_binaural_active(struct ast_bridge *bridge, unsigned int binaural_active)
Activates the use of binaural signals in a conference bridge.
Definition: bridge.c:3705
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612
struct ast_channel * chan
Definition: confbridge.h:279
int ast_audiohook_volume_get(struct ast_channel *chan, enum ast_audiohook_direction direction)
Retrieve the volume adjustment value on frames read from or written to a channel. ...
Definition: audiohook.c:1313
unsigned int playing_moh
Definition: confbridge.h:285
int pbx_exec(struct ast_channel *c, struct ast_app *app, const char *data)
Execute an application.
Definition: pbx_app.c:471
void ast_bridge_set_maximum_sample_rate(struct ast_bridge *bridge, unsigned int sample_rate)
Adjust the maximum mixing sample rate of a bridge used during multimix mode.
Definition: bridge.c:3719
Message representing attended transfer.
int ast_bridge_leave_hook(struct ast_bridge_features *features, ast_bridge_hook_callback callback, void *hook_pvt, ast_bridge_hook_pvt_destructor destructor, enum ast_bridge_hook_remove_flags remove_flags)
Attach a bridge channel leave hook to a bridge features structure.
Definition: bridge.c:3285
Structure that contains features information.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2958
struct confbridge_conference * conference
Definition: confbridge.h:274
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1101
const char * conf_get_sound(enum conf_sounds sound, struct bridge_profile_sounds *custom_sounds)
Looks to see if sound file is stored in bridge profile sounds, if not default sound is provided...
static void wait_for_initiator(struct ast_channel *initiator)
Wait for the initiator of an async playback to be ready.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
Definition: confbridge.h:138
#define OBJ_KEY
Definition: astobj2.h:1151
void ast_channel_unregister(const struct ast_channel_tech *tech)
Unregister a channel technology.
Definition: channel.c:570
static int play_sound_number(struct confbridge_conference *conference, int say_number)
Play number into the conference bridge.
int ast_bridge_join_hook(struct ast_bridge_features *features, ast_bridge_hook_callback callback, void *hook_pvt, ast_bridge_hook_pvt_destructor destructor, enum ast_bridge_hook_remove_flags remove_flags)
Attach a bridge channel join hook to a bridge features structure.
Definition: bridge.c:3275
static int playback_task(void *data)
Play an announcement into a confbridge.
#define OBJ_POINTER
Definition: astobj2.h:1150
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
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
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:3467
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
descriptor for a cli entry.
Definition: cli.h:171
void conf_remove_user_waiting(struct confbridge_conference *conference, struct confbridge_user *user)
Remove a conference bridge user from the waiting conference users in the conference.
Audiohooks Architecture.
static const int DEFAULT_SILENCE_THRESHOLD
The default silence threshold we will use if an alternate configured value is not present or is inval...
Definition: dsp.c:245
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
int ast_bridge_unsuspend(struct ast_bridge *bridge, struct ast_channel *chan)
Unsuspend a channel from a bridge.
Definition: bridge.c:3028
int ast_bridge_features_init(struct ast_bridge_features *features)
Initialize bridge features structure.
Definition: bridge.c:3620
struct ast_taskprocessor * ast_taskprocessor_get(const char *name, enum ast_tps_options create)
Get a reference to a taskprocessor with the specified name and create the taskprocessor if necessary...
int ast_bridge_talk_detector_hook(struct ast_bridge_features *features, ast_bridge_talking_indicate_callback callback, void *hook_pvt, ast_bridge_hook_pvt_destructor destructor, enum ast_bridge_hook_remove_flags remove_flags)
Attach a bridge channel talk detection hook to a bridge features structure.
Definition: bridge.c:3295
struct confbridge_user::@93 post_join_list
Structure that contains a snapshot of information about a bridge.
Definition: bridge.h:314
const struct user_profile * conf_find_user_profile(struct ast_channel *chan, const char *user_profile_name, struct user_profile *result)
find a user profile given a user profile's name and store that profile in result structure.
static int load_module(void)
Load the module.
static struct confbridge_conference * join_conference_bridge(const char *conference_name, struct confbridge_user *user)
Join a conference bridge.
Structure representing a snapshot of channel state.
struct stasis_message * ast_bridge_blob_create_from_snapshots(struct stasis_message_type *message_type, struct ast_bridge_snapshot *bridge_snapshot, struct ast_channel_snapshot *chan_snapshot, struct ast_json *blob)
Creates a ast_bridge_blob message from snapshots.
return a reference to a taskprocessor, create one if it does not exist
Definition: taskprocessor.h:76
Test Framework API.
unsigned int suspended_moh
Definition: confbridge.h:282
void ast_bridge_channel_feature_digit_add(struct ast_bridge_channel *bridge_channel, int digit)
Add a DTMF digit to the collected digits.
void conf_menu_entry_destroy(struct conf_menu_entry *menu_entry)
Destroys and frees all the actions stored in a menu_entry structure.
Structure for a data store type.
Definition: datastore.h:31
ast_channel_state
ast_channel states
Definition: channelstate.h:35
struct ast_channel_snapshot * target
void conf_mute_only_active(struct confbridge_conference *conference)
Attempt to mute/play MOH to the only user in the conference if they require it.
int ast_bridge_destroy(struct ast_bridge *bridge, int cause)
Destroy a bridge.
Definition: bridge.c:944
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
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
int ast_format_cap_append_by_type(struct ast_format_cap *cap, enum ast_media_type type)
Add all codecs Asterisk knows about for a specific type to the capabilities structure.
Definition: format_cap.c:216
void conf_update_user_mute(struct confbridge_user *user)
Update the actual mute status of the user and set it on the bridge.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
int ast_channel_get_up_time(struct ast_channel *chan)
Obtain how long it has been since the channel was answered.
Definition: channel.c:2845
struct confbridge_state * CONF_STATE_EMPTY
Conference state with no active or waiting users.
unsigned int silence_threshold
Definition: confbridge.h:164
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:3421
unsigned int binaural_suspended
int ast_channel_register(const struct ast_channel_tech *tech)
Register a channel technology (a new channel driver) Called by a channel module to register the kind ...
Definition: channel.c:539
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
Structure for a data store object.
Definition: datastore.h:64
struct confbridge_user::@94 list
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2399
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
void confbridge_handle_atxfer(struct ast_attended_transfer_message *msg)
Create join/leave events for attended transfers.
static const char app[]
conference_event_fn leave_unmarked
Definition: conf_state.h:50
void conf_add_user_marked(struct confbridge_conference *conference, struct confbridge_user *user)
Add a conference bridge user as a marked active user of the conference.
struct ast_channel * record_chan
Definition: confbridge.h:257
#define CONFERENCE_BRIDGE_BUCKETS
Out-of-call text message support.
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition: file.c:1141
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
Definition: linkedlists.h:856
#define AST_TASKPROCESSOR_MAX_NAME
Suggested maximum taskprocessor name length (less null terminator).
Definition: taskprocessor.h:61
struct ast_str * orig_rec_file
Definition: confbridge.h:259
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
unsigned int markedusers
Definition: confbridge.h:252
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:7776
void ast_free_ptr(void *ptr)
free() wrapper
Definition: main/astmm.c:1739
struct ast_channel_snapshot_dialplan * dialplan
struct ast_bridge_channel_snapshot_pair to_transferee
void ast_bridge_features_destroy(struct ast_bridge_features *features)
Destroy an allocated bridge features struct.
Definition: bridge.c:3674
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
static int hangup_playback(void *data)
Hang up the announcer channel.
conference_event_fn join_marked
Definition: conf_state.h:49
struct ast_bridge * bridge
Bridge this channel is participating in.
void async_play_sound_ready(struct ast_channel *chan)
Indicate the initiator of an async sound file is ready for it to play.
char dtmf[MAXIMUM_DTMF_FEATURE_STRING]
Definition: confbridge.h:140
struct confbridge_conference::@90 active_list
Media Stream API.
struct ast_taskprocessor * playback_queue
Definition: confbridge.h:262
int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar)
Simply remove extension from context.
Definition: pbx.c:4948
void ast_bridge_set_remb_estimated_bitrate(struct ast_bridge *bridge, float estimated_bitrate)
Force the REMB report estimated bitrate to a specific max value.
Definition: bridge.c:3807
#define OBJ_PARTIAL_KEY
Definition: astobj2.h:1152
struct bridge_profile b_profile
Definition: confbridge.h:250
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition: manager.c:3050
unsigned int locked
Definition: confbridge.h:254
static void destroy_conference_bridge(void *obj)
Destroy a conference bridge.
static int handle_conf_user_leave(struct confbridge_user *user)
Call the proper leave event handler for the user for the conference bridge's current state...
struct ast_channel_tech * conf_record_get_tech(void)
Get ConfBridge record channel technology struct.
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
void conf_handle_second_active(struct confbridge_conference *conference)
Handle when a conference moves to having more than one active participant.
unsigned int waitingusers
Definition: confbridge.h:253
static int setup_async_playback_datastore(struct ast_channel *initiator)
Prepare the async playback datastore.
int ast_record_review(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, const char *path)
Allow to record message and have a review option.
Definition: main/app.c:2646
void conf_bridge_profile_copy(struct bridge_profile *dst, struct bridge_profile *src)
copies a bridge profile
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
struct ast_party_id id
Caller party ID.
Definition: channel.h:420
struct ast_bridge_channel_snapshot_pair to_transfer_target
#define ast_custom_function_register_escalating(acf, escalation)
Register a custom function which requires escalated privileges.
Definition: pbx.h:1567
struct ast_flags feature_flags
Definition: pbx.h:214
unsigned int text_messaging
void ast_bridge_remove_video_src(struct ast_bridge *bridge, struct ast_channel *chan)
remove a channel as a source of video for the bridge.
Definition: bridge.c:3917
void ast_bridge_set_send_sdp_label(struct ast_bridge *bridge, unsigned int send_sdp_label)
Controls whether to send a "label" attribute in each stream in an SDP.
Definition: bridge.c:3966
conference_event_fn leave_marked
Definition: conf_state.h:52
static struct ast_datastore_info async_datastore_info
Datastore used for timing of async announcement playback.
struct ast_bridge_channel * bridge_channel
int conf_announce_channel_push(struct ast_channel *ast)
Push the announcer channel into the conference.
int async_play_sound_file(struct confbridge_conference *conference, const char *filename, struct ast_channel *initiator)
Play sound file into conference bridge asynchronously.
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
ast_mutex_t lock
int ast_bridge_impart(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, enum ast_bridge_impart_flags flags) attribute_warn_unused_result
Impart a channel to a bridge (non-blocking)
Definition: bridge.c:1878
void ast_brige_set_remb_behavior(struct ast_bridge *bridge, enum ast_bridge_video_sfu_remb_behavior behavior)
Set the REMB report generation behavior on a bridge.
Definition: bridge.c:3798
General Asterisk PBX channel definitions.
Asterisk JSON abstraction layer.
ast_bridge_hook_type
Asterisk file paths, configured in asterisk.conf.
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition: manager.c:3475
const ast_string_field appl
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel, struct confbridge_user *user, struct conf_menu_entry *menu_entry, struct conf_menu *menu)
Once a DTMF sequence matches a sequence in the user's DTMF menu, this function will get called to per...
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
struct ast_channel_snapshot * transferee
enum ast_pbx_result ast_pbx_run_args(struct ast_channel *c, struct ast_pbx_args *args)
Execute the PBX in the current thread.
Definition: pbx.c:4735
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
void conf_bridge_profile_destroy(struct bridge_profile *b_profile)
Destroy a bridge profile found by 'conf_find_bridge_profile'.
void conf_handle_first_join(struct confbridge_conference *conference)
Callback to execute any time we transition from zero to one active users.
#define ast_channel_cleanup(c)
Cleanup a channel reference.
Definition: channel.h:2969
#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
int conf_reload_config(void)
reload confbridge.conf file
In case you didn't read that giant block of text above the mansession_session struct, the mansession is named this solely to keep the API the same in Asterisk. This structure really represents data that is different from Manager action to Manager action. The mansession_session pointer contained within points to session-specific data.
Definition: manager.c:1785
struct ast_channel_tech * conf_announce_get_tech(void)
Get ConfBridge announce channel technology struct.
#define ast_format_cap_append(cap, format, framing)
Add format capability to capabilities structure.
Definition: format_cap.h:99
int ast_bridge_interval_hook(struct ast_bridge_features *features, enum ast_bridge_hook_timer_option flags, unsigned int interval, ast_bridge_hook_callback callback, void *hook_pvt, ast_bridge_hook_pvt_destructor destructor, enum ast_bridge_hook_remove_flags remove_flags)
Attach an interval hook to a bridge features structure.
Definition: bridge.c:3319
char * ast_strsep(char **s, const char sep, uint32_t flags)
Act like strsep but ignore separators inside quotes.
Definition: utils.c:1835
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define MAXIMUM_DTMF_FEATURE_STRING
Maximum length of a DTMF feature string.
#define ast_debug(level,...)
Log a DEBUG message.
struct ao2_container * conference_bridges
Container to hold all conference bridges in progress.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
char language[MAX_LANGUAGE]
Definition: confbridge.h:229
struct ast_bridge * ast_bridge_base_new(uint32_t capabilities, unsigned int flags, const char *creator, const char *name, const char *id)
Create a new base class bridge.
Definition: bridge.c:934
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
#define ast_format_cap_alloc(flags)
Allocate a new ast_format_cap structure.
Definition: format_cap.h:49
unsigned int maximum_sample_rate
Definition: confbridge.h:236
struct ast_str * ast_manager_build_channel_state_string(const struct ast_channel_snapshot *snapshot)
Generate the AMI message body from a channel snapshot.
Structure to describe a channel "technology", ie a channel driver See for examples: ...
Definition: channel.h:628
Core PBX routines and definitions.
unsigned int internal_sample_rate
Definition: confbridge.h:235
int ast_queue_frame(struct ast_channel *chan, struct ast_frame *f)
Queue one or more frames to a channel's frame queue.
Definition: channel.c:1139
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
void ast_bridge_set_video_update_discard(struct ast_bridge *bridge, unsigned int video_update_discard)
Set the amount of time to discard subsequent video updates after a video update has been sent...
Definition: bridge.c:3782
struct confbridge_state * state
Definition: confbridge.h:248
#define ast_test_suite_event_notify(s, f,...)
Definition: test.h:189
struct stasis_topic * ast_channel_topic(struct ast_channel *chan)
A topic which publishes the events for a particular channel.
unsigned int dtmf_passthrough
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:8057
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
void conf_moh_start(struct confbridge_user *user)
Start MOH for the conference user.
unsigned int video_update_discard
Definition: confbridge.h:240
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
Support for dynamic strings.
Definition: strings.h:623
Format capabilities structure, holds formats + preference order + etc.
Definition: format_cap.c:54
#define RECORD_FILENAME_INITIAL_SPACE
void conf_destroy_config(void)
destroy the information loaded from the confbridge.conf file
#define ao2_unlink(container, obj)
Remove an object from a container.
Definition: astobj2.h:1578
int conf_handle_only_person(struct confbridge_user *user)
Handle actions whenever an user joins an empty conference.
#define AST_DIGIT_NONE
Definition: file.h:47
static int async_playback_task(void *data)
Play an announcement into a confbridge asynchronously.
unsigned int muted
Definition: confbridge.h:283
int ast_bridge_is_video_src(struct ast_bridge *bridge, struct ast_channel *chan)
Determine if a channel is a video src for the bridge.
Definition: bridge.c:3891
int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime_sec, const char *fmt, int *duration, int *sound_duration, int silencethreshold, int maxsilence_ms, const char *path)
Record a file based on input from a channel. Use default accept and cancel DTMF. This function will p...
Definition: main/app.c:2154
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_bridge_set_remb_send_interval(struct ast_bridge *bridge, unsigned int remb_send_interval)
Set the interval at which a combined REMB frame will be sent to video sources.
Definition: bridge.c:3789
#define AST_LIST_LAST(head)
Returns the last entry contained in a list.
Definition: linkedlists.h:429
void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state, int cause)
Set bridge channel state to leave bridge (if not leaving already).
void conf_moh_stop(struct confbridge_user *user)
Stop MOH for the conference user.
struct ast_format_cap * capabilities
Definition: channel.h:632
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
unsigned int max_members
Definition: confbridge.h:234
void conf_add_user_waiting(struct confbridge_conference *conference, struct confbridge_user *user)
Add a conference bridge user as an waiting user of the conference.
unsigned int talking_threshold
Definition: confbridge.h:162
void manager_confbridge_shutdown(void)
unregister stasis message routers to handle manager events for confbridge messages ...
#define ast_bridge_unlock(bridge)
Unlock the bridge.
Definition: bridge.h:481
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition: stasis.c:1511
int conf_set_menu_to_user(struct ast_channel *chan, struct confbridge_user *user, const char *menu_profile_name)
find a menu profile given a menu profile's name and apply the menu in DTMF hooks. ...
#define SCOPED_AO2LOCK(varname, obj)
scoped lock specialization for ao2 mutexes.
Definition: lock.h:604
char * command
Definition: cli.h:186
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
conference_event_fn join_waitmarked
Definition: conf_state.h:48
struct ast_bridge_snapshot * ast_bridge_snapshot_create(struct ast_bridge *bridge)
Generate a snapshot of the bridge state. This is an ao2 object, so ao2_cleanup() to deallocate...
unsigned int timeout
Definition: confbridge.h:166
int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
stream file until digit If the file name is non-empty, try to play it.
Definition: file.c:1878
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2541
void ast_bridge_set_talker_src_video_mode(struct ast_bridge *bridge)
Set the bridge to pick the strongest talker supporting video as the single source video feed...
Definition: bridge.c:3766
struct bridge_profile b_profile
Definition: confbridge.h:275
struct ast_bridge_features features
Definition: confbridge.h:280
static int push_announcer(struct confbridge_conference *conference)
Push the announcer channel into the bridge.
The structure that represents a conference bridge.
Definition: confbridge.h:246
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
unsigned int activeusers
Definition: confbridge.h:251
int ast_audiohook_volume_adjust(struct ast_channel *chan, enum ast_audiohook_direction direction, int volume)
Adjust the volume on frames read from or written to a channel.
Definition: audiohook.c:1333
An API for managing task processing threads that can be shared across modules.
int conf_handle_inactive_waitmarked(struct confbridge_user *user)
Handle actions every time a waitmarked user joins w/o a marked user present.
int conf_find_menu_entry_by_sequence(const char *dtmf_sequence, struct conf_menu *menu, struct conf_menu_entry *result)
Finds a menu_entry in a menu structure matched by DTMF sequence.
void conf_send_event_to_participants(struct confbridge_conference *conference, struct ast_channel *chan, struct stasis_message *msg)
Send events to bridge participants.
structure to hold users read from users.conf
int ast_bridge_join(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, struct ast_bridge_tech_optimizations *tech_args, enum ast_bridge_join_flags flags)
Join a channel to a bridge (blocking)
Definition: bridge.c:1621
void conf_invalid_event_fn(struct confbridge_user *user)
A handler for join/leave events that are invalid in a particular state.
Definition: conf_state.c:42
void ast_bridge_set_single_src_video_mode(struct ast_bridge *bridge, struct ast_channel *video_src_chan)
Set a bridge to feed a single video source to all participants.
Definition: bridge.c:3749
const char * usage
Definition: cli.h:177
struct stasis_topic * ast_bridge_topic(struct ast_bridge *bridge)
A topic which publishes the events for a particular bridge.
char name_rec_location[PATH_MAX]
Definition: confbridge.h:278
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...
struct ast_bridge_features * ast_bridge_features_new(void)
Allocate a new bridge features struct.
Definition: bridge.c:3683
#define ast_bridge_lock(bridge)
Lock the bridge.
Definition: bridge.h:470
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:693
#define AST_YESNO(x)
return Yes or No depending on the argument.
Definition: strings.h:143
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
void * data
Definition: datastore.h:66
static int unload_module(void)
Called when module is being unloaded.
static int conference_bridge_hash_cb(const void *obj, const int flags)
Hashing function used for conference bridges container.
struct ast_channel * chan
Structure that contains information regarding a channel in a bridge.
int ast_taskprocessor_push(struct ast_taskprocessor *tps, int(*task_exe)(void *datap), void *datap) attribute_warn_unused_result
Push a task into the specified taskprocessor queue and signal the taskprocessor thread.
char menu_name[MAX_PROFILE_NAME]
Definition: confbridge.h:277
#define ast_channel_ref(c)
Increase channel reference count.
Definition: channel.h:2947
conference_event_fn leave_waitmarked
Definition: conf_state.h:51
int ast_add_extension(const char *context, int replace, const char *extension, int priority, const char *label, const char *callerid, const char *application, void *data, void(*datad)(void *), const char *registrar)
Add and extension to an extension context.
Definition: pbx.c:6928
struct ast_channel_snapshot * ast_channel_snapshot_get_latest(const char *uniqueid)
Obtain the latest ast_channel_snapshot from the Stasis Message Bus API cache. This is an ao2 object...
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
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
Standard Command Line Interface.
void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel)
Lock the bridge associated with the bridge channel.
int ast_bridge_suspend(struct ast_bridge *bridge, struct ast_channel *chan)
Suspend a channel temporarily from a bridge.
Definition: bridge.c:3007
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
#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
void * ast_taskprocessor_unreference(struct ast_taskprocessor *tps)
Unreference the specified taskprocessor and its reference count will decrement.
ast_app: A registered application
Definition: pbx_app.c:45
static int async_delete_name_rec_task(void *data)
Delete user's name file asynchronously.
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
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_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2805
unsigned int remb_estimated_bitrate
Definition: confbridge.h:242
struct confbridge_conference * conf_find_bridge(const char *conference_name)
Find a confbridge by name.
struct ast_channel * playback_chan
Definition: confbridge.h:256
Data structure associated with a single frame of data.
Abstract JSON element (object, array, string, int, ...).
The structure that represents a conference bridge user.
Definition: confbridge.h:273
struct confbridge_conference::@91 waiting_list
unsigned int muted
Definition: confbridge.h:255
void conf_ended(struct confbridge_conference *conference)
Callback to be called when the conference has become empty.
struct ast_str * record_filename
Definition: confbridge.h:258
char * ast_str_set_substr(struct ast_str **buf, ssize_t maxlen, const char *src, size_t maxsrc)
Set a dynamic string to a non-NULL terminated substring.
Definition: strings.h:1055
static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
Comparison function used for conference bridges container.
static int play_file(struct ast_bridge_channel *bridge_channel, struct ast_channel *channel, const char *filename)
Playback the given filename and monitor for any dtmf interrupts.
int conf_load_config(void)
load confbridge.conf file
int play_sound_file(struct confbridge_conference *conference, const char *filename)
Play sound file into conference bridge.
struct user_profile u_profile
Definition: confbridge.h:276
unsigned int talking
Definition: confbridge.h:286
Generic container type.
#define AST_CLI_YESNO(x)
Return Yes or No depending on the argument.
Definition: cli.h:71
static int play_prompt_to_user(struct confbridge_user *user, const char *filename)
Play back an audio file to a channel.
static int handle_conf_user_join(struct confbridge_user *user)
Call the proper join event handler for the user for the conference bridge's current state...
struct ast_app * pbx_findapp(const char *app)
Look up an application.
Definition: ael_main.c:165
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:191
Say numbers and dates (maybe words one day too)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
unsigned int remb_send_interval
Definition: confbridge.h:241
Bridging API.
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
Definition: dsp.c:2009
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:3389
Asterisk module definitions.
char name[MAX_CONF_NAME]
Definition: confbridge.h:247
struct stasis_message * ast_bridge_blob_create(struct stasis_message_type *message_type, struct ast_bridge *bridge, struct ast_channel *chan, struct ast_json *blob)
Creates a ast_bridge_blob message.
#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
unsigned int mix_interval
Definition: confbridge.h:237
int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
executes a write operation on a function
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2385
struct conf_menu_entry::@87 actions
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:297
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
unsigned int kicked
Definition: confbridge.h:284
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:222
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
Structure for mutex and tracking information.
Definition: lock.h:135
static force_inline int attribute_pure ast_str_case_hash(const char *str)
Compute a hash value on a case-insensitive string.
Definition: strings.h:1303
static int confbridge_exec(struct ast_channel *chan, const char *data)
The ConfBridge application.
struct ast_bridge_tech_optimizations tech_args
Definition: confbridge.h:281
void ast_bridge_set_internal_sample_rate(struct ast_bridge *bridge, unsigned int sample_rate)
Adjust the internal mixing sample rate of a bridge used during multimix mode.
Definition: bridge.c:3712
const ast_string_field name
Media Format Cache API.
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
#define AST_APP_ARG(name)
Define an application argument.
static void leave_conference(struct confbridge_user *user)
Leave a conference.
struct ast_bridge * bridge
Definition: confbridge.h:249
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: utils.c:2479
const struct bridge_profile * conf_find_bridge_profile(struct ast_channel *chan, const char *bridge_profile_name, struct bridge_profile *result)
Find a bridge profile given a bridge profile's name and store that profile in result structure...
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:3431
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:342
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532