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