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