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