Wed Jan 8 2020 09:49:39

Asterisk developer's documentation


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  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*! \file
20  *
21  * \brief Conference Bridge application
22  *
23  * \author\verbatim Joshua Colp <jcolp@digium.com> \endverbatim
24  *
25  * This is a conference bridge application utilizing the bridging core.
26  * \ingroup applications
27  */
28 
29 /*** MODULEINFO
30  <support_level>extended</support_level>
31  ***/
32 
33 #include "asterisk.h"
34 
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 415060 $")
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <string.h>
41 #include <signal.h>
42 
43 #include "asterisk/file.h"
44 #include "asterisk/logger.h"
45 #include "asterisk/channel.h"
46 #include "asterisk/pbx.h"
47 #include "asterisk/module.h"
48 #include "asterisk/lock.h"
49 #include "asterisk/app.h"
50 #include "asterisk/bridging.h"
51 #include "asterisk/musiconhold.h"
52 #include "asterisk/say.h"
53 #include "asterisk/audiohook.h"
54 #include "asterisk/astobj2.h"
55 
56 /*** DOCUMENTATION
57  <application name="ConfBridge" language="en_US">
58  <synopsis>
59  Conference bridge application.
60  </synopsis>
61  <syntax>
62  <parameter name="confno">
63  <para>The conference number</para>
64  </parameter>
65  <parameter name="options">
66  <optionlist>
67  <option name="a">
68  <para>Set admin mode.</para>
69  </option>
70  <option name="A">
71  <para>Set marked mode.</para>
72  </option>
73  <option name="c">
74  <para>Announce user(s) count on joining a conference.</para>
75  </option>
76  <option name="m">
77  <para>Set initially muted.</para>
78  </option>
79  <option name="M" hasparams="optional">
80  <para>Enable music on hold when the conference has a single caller. Optionally,
81  specify a musiconhold class to use. If one is not provided, it will use the
82  channel's currently set music class, or <literal>default</literal>.</para>
83  <argument name="class" required="true" />
84  </option>
85  <option name="1">
86  <para>Do not play message when first person enters</para>
87  </option>
88  <option name="s">
89  <para>Present menu (user or admin) when <literal>#</literal> is received
90  (send to menu).</para>
91  </option>
92  <option name="w">
93  <para>Wait until the marked user enters the conference.</para>
94  </option>
95  <option name="q">
96  <para>Quiet mode (don't play enter/leave sounds).</para>
97  </option>
98  </optionlist>
99  </parameter>
100  </syntax>
101  <description>
102  <para>Enters the user into a specified conference bridge. The user can exit the conference by hangup only.</para>
103  <para>The join sound can be set using the <literal>CONFBRIDGE_JOIN_SOUND</literal> variable and the leave sound can be set using the <literal>CONFBRIDGE_LEAVE_SOUND</literal> variable. These can be unique to the caller.</para>
104  <note><para>This application will not automatically answer the channel.</para></note>
105  </description>
106  </application>
107 ***/
108 
109 /*!
110  * \par Playing back a file to a channel in a conference
111  * You might notice in this application that while playing a sound file
112  * to a channel the actual conference bridge lock is not held. This is done so
113  * that other channels are not blocked from interacting with the conference bridge.
114  * Unfortunately because of this it is possible for things to change after the sound file
115  * is done being played. Data must therefore be checked after reacquiring the conference
116  * bridge lock if it is important.
117  */
118 
119 static const char app[] = "ConfBridge";
120 
121 enum {
122  OPTION_ADMIN = (1 << 0), /*!< Set if the caller is an administrator */
123  OPTION_MENU = (1 << 1), /*!< Set if the caller should have access to the conference bridge IVR menu */
124  OPTION_MUSICONHOLD = (1 << 2), /*!< Set if music on hold should be played if nobody else is in the conference bridge */
125  OPTION_NOONLYPERSON = (1 << 3), /*!< Set if the "you are currently the only person in this conference" sound file should not be played */
126  OPTION_STARTMUTED = (1 << 4), /*!< Set if the caller should be initially set muted */
127  OPTION_ANNOUNCEUSERCOUNT = (1 << 5), /*!< Set if the number of users should be announced to the caller */
128  OPTION_MARKEDUSER = (1 << 6), /*!< Set if the caller is a marked user */
129  OPTION_WAITMARKED = (1 << 7), /*!< Set if the conference must wait for a marked user before starting */
130  OPTION_QUIET = (1 << 8), /*!< Set if no audio prompts should be played */
131 };
132 
133 enum {
134  OPTION_MUSICONHOLD_CLASS, /*!< If the 'M' option is set, the music on hold class to play */
135  /*This must be the last element */
137 };
138 
149 });
150 
151 /* Maximum length of a conference bridge name */
152 #define MAX_CONF_NAME AST_MAX_EXTENSION
153 
154 /* Number of buckets our conference bridges container can have */
155 #define CONFERENCE_BRIDGE_BUCKETS 53
156 
157 /*! \brief The structure that represents a conference bridge */
159  char name[MAX_CONF_NAME]; /*!< Name of the conference bridge */
160  struct ast_bridge *bridge; /*!< Bridge structure doing the mixing */
161  unsigned int users; /*!< Number of users present */
162  unsigned int markedusers; /*!< Number of marked users present */
163  unsigned int locked:1; /*!< Is this conference bridge locked? */
164  AST_LIST_HEAD_NOLOCK(, conference_bridge_user) users_list; /*!< List of users participating in the conference bridge */
165  struct ast_channel *playback_chan; /*!< Channel used for playback into the conference bridge */
166  ast_mutex_t playback_lock; /*!< Lock used for playback channel */
167 };
168 
169 /*! \brief The structure that represents a conference bridge user */
171  struct conference_bridge *conference_bridge; /*!< Conference bridge they are participating in */
172  struct ast_channel *chan; /*!< Asterisk channel participating */
173  struct ast_flags flags; /*!< Flags passed in when the application was called */
174  char *opt_args[OPTION_ARRAY_SIZE]; /*!< Arguments to options passed when application was called */
175  struct ast_bridge_features features; /*!< Bridge features structure */
176  unsigned int kicked:1; /*!< User has been kicked from the conference */
177  AST_LIST_ENTRY(conference_bridge_user) list; /*!< Linked list information */
178 };
179 
180 /*! \brief Container to hold all conference bridges in progress */
182 
183 static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename);
184 
185 /*! \brief Hashing function used for conference bridges container */
186 static int conference_bridge_hash_cb(const void *obj, const int flags)
187 {
188  const struct conference_bridge *conference_bridge = obj;
189  return ast_str_case_hash(conference_bridge->name);
190 }
191 
192 /*! \brief Comparison function used for conference bridges container */
193 static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
194 {
195  const struct conference_bridge *conference_bridge0 = obj, *conference_bridge1 = arg;
196  return (!strcasecmp(conference_bridge0->name, conference_bridge1->name) ? CMP_MATCH | CMP_STOP : 0);
197 }
198 
199 /*!
200  * \brief Announce number of users in the conference bridge to the caller
201  *
202  * \param conference_bridge Conference bridge to peek at
203  * \param conference_bridge_user Caller
204  *
205  * \return Returns 0 on success, -1 if the user hung up
206  */
207 static int announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
208 {
209  if (conference_bridge->users == 1) {
210  /* Awww we are the only person in the conference bridge */
211  return 0;
212  } else if (conference_bridge->users == 2) {
213  /* Eep, there is one other person */
214  if (ast_stream_and_wait(conference_bridge_user->chan, "conf-onlyone", "")) {
215  return -1;
216  }
217  } else {
218  /* Alas multiple others in here */
219  if (ast_stream_and_wait(conference_bridge_user->chan, "conf-thereare", "")) {
220  return -1;
221  }
222  if (ast_say_number(conference_bridge_user->chan, conference_bridge->users - 1, "", conference_bridge_user->chan->language, NULL)) {
223  return -1;
224  }
225  if (ast_stream_and_wait(conference_bridge_user->chan, "conf-otherinparty", "")) {
226  return -1;
227  }
228  }
229  return 0;
230 }
231 
232 /*!
233  * \brief Play back an audio file to a channel
234  *
235  * \param conference_bridge Conference bridge they are in
236  * \param chan Channel to play audio prompt to
237  * \param file Prompt to play
238  *
239  * \return Returns 0 on success, -1 if the user hung up
240  *
241  * \note This function assumes that conference_bridge is locked
242  */
243 static int play_prompt_to_channel(struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file)
244 {
245  int res;
246  ao2_unlock(conference_bridge);
247  res = ast_stream_and_wait(chan, file, "");
248  ao2_lock(conference_bridge);
249  return res;
250 }
251 
252 /*!
253  * \brief Perform post-joining marked specific actions
254  *
255  * \param conference_bridge Conference bridge being joined
256  * \param conference_bridge_user Conference bridge user joining
257  *
258  * \return Returns 0 on success, -1 if the user hung up
259  */
260 static int post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
261 {
262  if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
263  struct conference_bridge_user *other_conference_bridge_user = NULL;
264 
265  /* If we are not the first marked user to join just bail out now */
266  if (conference_bridge->markedusers >= 2) {
267  return 0;
268  }
269 
270  /* Iterate through every participant stopping MOH on them if need be */
271  AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
272  if (other_conference_bridge_user == conference_bridge_user) {
273  continue;
274  }
275  if (ast_test_flag(&other_conference_bridge_user->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan)) {
276  ast_moh_stop(other_conference_bridge_user->chan);
277  ast_bridge_unsuspend(conference_bridge->bridge, other_conference_bridge_user->chan);
278  }
279  }
280 
281  /* Next play the audio file stating they are going to be placed into the conference */
282  if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
283  if (play_prompt_to_channel(conference_bridge,
284  conference_bridge_user->chan,
285  "conf-placeintoconf")) {
286  /* user hungup while the sound was playing */
287  return -1;
288  }
289  }
290 
291  /* Finally iterate through and unmute them all */
292  AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
293  if (other_conference_bridge_user == conference_bridge_user) {
294  continue;
295  }
296  other_conference_bridge_user->features.mute = 0;
297  }
298 
299  } else {
300  /* If a marked user already exists in the conference bridge we can just bail out now */
301  if (conference_bridge->markedusers) {
302  return 0;
303  }
304  /* Be sure we are muted so we can't talk to anybody else waiting */
305  conference_bridge_user->features.mute = 1;
306  /* If we have not been quieted play back that they are waiting for the leader */
307  if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
308  if (play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-waitforleader")) {
309  /* user hung up while the sound was playing */
310  return -1;
311  }
312  }
313  /* Start music on hold if needed */
314  /* We need to recheck the markedusers value here. play_prompt_to_channel unlocks the conference bridge, potentially
315  * allowing a marked user to enter while the prompt was playing
316  */
317  if (!conference_bridge->markedusers && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
318  ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
319  }
320  }
321  return 0;
322 }
323 
324 /*!
325  * \brief Perform post-joining non-marked specific actions
326  *
327  * \param conference_bridge Conference bridge being joined
328  * \param conference_bridge_user Conference bridge user joining
329  *
330  * \return Returns 0 on success, -1 if the user hung up
331  */
332 static int post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
333 {
334  /* Play back audio prompt and start MOH if need be if we are the first participant */
335  if (conference_bridge->users == 1) {
336  /* If audio prompts have not been quieted or this prompt quieted play it on out */
337  if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET | OPTION_NOONLYPERSON)) {
338  if (play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-onlyperson")) {
339  /* user hung up while the sound was playing */
340  return -1;
341  }
342  }
343  /* If we need to start music on hold on the channel do so now */
344  /* We need to re-check the number of users in the conference bridge here because another conference bridge
345  * participant could have joined while the above prompt was playing for the first user.
346  */
347  if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
348  ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
349  }
350  return 0;
351  }
352 
353  /* Announce number of users if need be */
354  if (ast_test_flag(&conference_bridge_user->flags, OPTION_ANNOUNCEUSERCOUNT)) {
355  ao2_unlock(conference_bridge);
356  if (announce_user_count(conference_bridge, conference_bridge_user)) {
357  ao2_lock(conference_bridge);
358  return -1;
359  }
360  ao2_lock(conference_bridge);
361  }
362 
363  /* If we are the second participant we may need to stop music on hold on the first */
364  if (conference_bridge->users == 2) {
365  struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
366 
367  /* Temporarily suspend the above participant from the bridge so we have control to stop MOH if needed */
368  if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
369  ast_moh_stop(first_participant->chan);
370  ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
371  }
372  }
373  return 0;
374 }
375 
376 /*!
377  * \brief Destroy a conference bridge
378  *
379  * \param obj The conference bridge object
380  *
381  * \return Returns nothing
382  */
383 static void destroy_conference_bridge(void *obj)
384 {
385  struct conference_bridge *conference_bridge = obj;
386 
387  ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name);
388 
389  ast_mutex_destroy(&conference_bridge->playback_lock);
390 
391  if (conference_bridge->playback_chan) {
392  struct ast_channel *underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
393  ast_hangup(underlying_channel);
394  ast_hangup(conference_bridge->playback_chan);
395  conference_bridge->playback_chan = NULL;
396  }
397 
398  /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
399  if (conference_bridge->bridge) {
400  ast_bridge_destroy(conference_bridge->bridge);
401  conference_bridge->bridge = NULL;
402  }
403 }
404 
405 static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user);
406 
407 /*!
408  * \brief Join a conference bridge
409  *
410  * \param name The conference name
411  * \param conference_bridge_user Conference bridge user structure
412  *
413  * \return A pointer to the conference bridge struct, or NULL if the conference room wasn't found.
414  */
415 static struct conference_bridge *join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user)
416 {
417  struct conference_bridge *conference_bridge = NULL;
418  struct conference_bridge tmp;
419 
420  ast_copy_string(tmp.name, name, sizeof(tmp.name));
421 
422  /* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */
424 
425  ast_debug(1, "Trying to find conference bridge '%s'\n", name);
426 
427  /* Attempt to find an existing conference bridge */
428  conference_bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
429 
430  /* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */
431  if (conference_bridge && conference_bridge->locked && !ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN)) {
433  ao2_ref(conference_bridge, -1);
434  ast_debug(1, "Conference bridge '%s' is locked and caller is not an admin\n", name);
435  ast_stream_and_wait(conference_bridge_user->chan, "conf-locked", "");
436  return NULL;
437  }
438 
439  /* If no conference bridge was found see if we can create one */
440  if (!conference_bridge) {
441  /* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */
442  if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) {
444  ast_log(LOG_ERROR, "Conference bridge '%s' does not exist.\n", name);
445  return NULL;
446  }
447 
448  /* Setup conference bridge parameters */
449  ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
450 
451  /* Create an actual bridge that will do the audio mixing */
453  ao2_ref(conference_bridge, -1);
454  conference_bridge = NULL;
456  ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name);
457  return NULL;
458  }
459 
460  /* Setup lock for playback channel */
461  ast_mutex_init(&conference_bridge->playback_lock);
462 
463  /* Link it into the conference bridges container */
464  ao2_link(conference_bridges, conference_bridge);
465 
466  ast_debug(1, "Created conference bridge '%s' and linked to container '%p'\n", name, conference_bridges);
467  }
468 
470 
471  /* Setup conference bridge user parameters */
472  conference_bridge_user->conference_bridge = conference_bridge;
473 
474  ao2_lock(conference_bridge);
475 
476  /* All good to go, add them in */
477  AST_LIST_INSERT_TAIL(&conference_bridge->users_list, conference_bridge_user, list);
478 
479  /* Increment the users count on the bridge, but record it as it is going to need to be known right after this */
480  conference_bridge->users++;
481 
482  /* If the caller is a marked user bump up the count */
483  if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
484  conference_bridge->markedusers++;
485  }
486 
487  /* Set the device state for this conference */
488  if (conference_bridge->users == 1) {
489  ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "confbridge:%s", conference_bridge->name);
490  }
491 
492  /* If the caller is a marked user or is waiting for a marked user to enter pass 'em off, otherwise pass them off to do regular joining stuff */
493  if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER | OPTION_WAITMARKED)) {
494  if (post_join_marked(conference_bridge, conference_bridge_user)) {
495  ao2_unlock(conference_bridge);
496  leave_conference_bridge(conference_bridge, conference_bridge_user);
497  return NULL;
498  }
499  } else {
500  if (post_join_unmarked(conference_bridge, conference_bridge_user)) {
501  ao2_unlock(conference_bridge);
502  leave_conference_bridge(conference_bridge, conference_bridge_user);
503  return NULL;
504  }
505  }
506 
507  ao2_unlock(conference_bridge);
508 
509  return conference_bridge;
510 }
511 
512 /*!
513  * \brief Leave a conference bridge
514  *
515  * \param conference_bridge The conference bridge to leave
516  * \param conference_bridge_user The conference bridge user structure
517  *
518  */
519 static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
520 {
521  ao2_lock(conference_bridge);
522 
523  /* If this caller is a marked user bump down the count */
524  if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
525  conference_bridge->markedusers--;
526  }
527 
528  /* Decrement the users count while keeping the previous participant count */
529  conference_bridge->users--;
530 
531  /* Drop conference bridge user from the list, they be going bye bye */
532  AST_LIST_REMOVE(&conference_bridge->users_list, conference_bridge_user, list);
533 
534  /* If there are still users in the conference bridge we may need to do things (such as start MOH on them) */
535  if (conference_bridge->users) {
536  if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER) && !conference_bridge->markedusers) {
537  struct conference_bridge_user *other_participant = NULL;
538 
539  /* Start out with muting everyone */
540  AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
541  other_participant->features.mute = 1;
542  }
543 
544  /* Play back the audio prompt saying the leader has left the conference */
545  if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
546  ao2_unlock(conference_bridge);
547  ast_autoservice_start(conference_bridge_user->chan);
548  play_sound_file(conference_bridge, "conf-leaderhasleft");
549  ast_autoservice_stop(conference_bridge_user->chan);
550  ao2_lock(conference_bridge);
551  }
552 
553  /* Now on to starting MOH if needed */
554  AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
555  if (ast_test_flag(&other_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) {
556  ast_moh_start(other_participant->chan, other_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
557  ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan);
558  }
559  }
560  } else if (conference_bridge->users == 1) {
561  /* Of course if there is one other person in here we may need to start up MOH on them */
562  struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
563 
564  if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
565  ast_moh_start(first_participant->chan, first_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
566  ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
567  }
568  }
569  } else {
570  /* Set device state to "not in use" */
571  ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "confbridge:%s", conference_bridge->name);
572 
573  ao2_unlink(conference_bridges, conference_bridge);
574  }
575 
576  /* Done mucking with the conference bridge, huzzah */
577  ao2_unlock(conference_bridge);
578 
579  ao2_ref(conference_bridge, -1);
580 }
581 
582 /*!
583  * \brief Play sound file into conference bridge
584  *
585  * \param conference_bridge The conference bridge to play sound file into
586  * \param filename Sound file to play
587  *
588  * \retval 0 success
589  * \retval -1 failure
590  */
591 static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename)
592 {
593  struct ast_channel *underlying_channel;
594 
595  ast_mutex_lock(&conference_bridge->playback_lock);
596 
597  if (!(conference_bridge->playback_chan)) {
598  int cause;
599 
600  if (!(conference_bridge->playback_chan = ast_request("Bridge", AST_FORMAT_SLINEAR, NULL, "", &cause))) {
601  ast_mutex_unlock(&conference_bridge->playback_lock);
602  return -1;
603  }
604 
605  conference_bridge->playback_chan->bridge = conference_bridge->bridge;
606 
607  if (ast_call(conference_bridge->playback_chan, "", 0)) {
608  ast_hangup(conference_bridge->playback_chan);
609  conference_bridge->playback_chan = NULL;
610  ast_mutex_unlock(&conference_bridge->playback_lock);
611  return -1;
612  }
613 
614  ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name);
615 
616  underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
617  } else {
618  /* Channel was already available so we just need to add it back into the bridge */
619  underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
620  ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL);
621  }
622 
623  /* The channel is all under our control, in goes the prompt */
624  ast_stream_and_wait(conference_bridge->playback_chan, filename, "");
625 
626  ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", underlying_channel->name, conference_bridge->bridge);
627  ast_bridge_depart(conference_bridge->bridge, underlying_channel);
628 
629  ast_mutex_unlock(&conference_bridge->playback_lock);
630 
631  return 0;
632 }
633 
634 /*!
635  * \brief DTMF Menu Callback
636  *
637  * \param bridge Bridge this is involving
638  * \param bridge_channel Bridged channel this is involving
639  * \param hook_pvt User's conference bridge structure
640  *
641  * \retval 0 success
642  * \retval -1 failure
643  */
644 static int menu_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
645 {
646  struct conference_bridge_user *conference_bridge_user = hook_pvt;
647  struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge;
648  int digit, res = 0, isadmin = ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN);
649 
650  /* See if music on hold is playing */
651  ao2_lock(conference_bridge);
652  if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
653  /* Just us so MOH is probably indeed going, let's stop it */
654  ast_moh_stop(bridge_channel->chan);
655  }
656  ao2_unlock(conference_bridge);
657 
658  /* Try to play back the user menu, if it fails pass this back up so the bridging core will act on it */
659  if (ast_streamfile(bridge_channel->chan, (isadmin ? "conf-adminmenu" : "conf-usermenu"), bridge_channel->chan->language)) {
660  res = -1;
661  goto finished;
662  }
663 
664  /* Wait for them to enter a digit from the user menu options */
665  digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY);
666  ast_stopstream(bridge_channel->chan);
667 
668  if (digit == '1') {
669  /* 1 - Mute or unmute yourself, note we only allow manipulation if they aren't waiting for a marked user or if marked users exist */
670  if (!ast_test_flag(&conference_bridge_user->flags, OPTION_WAITMARKED) || conference_bridge->markedusers) {
671  conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0);
672  }
673  res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge_user->features.mute ? "conf-muted" : "conf-unmuted"), "");
674  } else if (isadmin && digit == '2') {
675  /* 2 - Unlock or lock conference */
676  conference_bridge->locked = (!conference_bridge->locked ? 1 : 0);
677  res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge->locked ? "conf-lockednow" : "conf-unlockednow"), "");
678  } else if (isadmin && digit == '3') {
679  /* 3 - Eject last user */
680  struct conference_bridge_user *last_participant = NULL;
681 
682  ao2_lock(conference_bridge);
683  if (((last_participant = AST_LIST_LAST(&conference_bridge->users_list)) == conference_bridge_user) || (ast_test_flag(&last_participant->flags, OPTION_ADMIN))) {
684  ao2_unlock(conference_bridge);
685  res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
686  } else {
687  last_participant->kicked = 1;
688  ast_bridge_remove(conference_bridge->bridge, last_participant->chan);
689  ao2_unlock(conference_bridge);
690  }
691  } else if (digit == '4') {
692  /* 4 - Decrease listening volume */
693  ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, -1);
694  } else if (digit == '6') {
695  /* 6 - Increase listening volume */
697  } else if (digit == '7') {
698  /* 7 - Decrease talking volume */
699  ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, -1);
700  } else if (digit == '8') {
701  /* 8 - Exit the IVR */
702  } else if (digit == '9') {
703  /* 9 - Increase talking volume */
705  } else {
706  /* No valid option was selected */
707  res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
708  }
709 
710  finished:
711  /* See if music on hold needs to be started back up again */
712  ao2_lock(conference_bridge);
713  if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
714  ast_moh_start(bridge_channel->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
715  }
716  ao2_unlock(conference_bridge);
717 
718  bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
719 
720  return res;
721 }
722 
723 /*! \brief The ConfBridge application */
724 static int confbridge_exec(struct ast_channel *chan, const char *data)
725 {
726  int res = 0, volume_adjustments[2];
727  char *parse;
728  struct conference_bridge *conference_bridge = NULL;
729  struct conference_bridge_user conference_bridge_user = {
730  .chan = chan,
731  };
732  const char *tmp, *join_sound = NULL, *leave_sound = NULL;
734  AST_APP_ARG(conf_name);
735  AST_APP_ARG(options);
736  );
737 
738  /* We need to make a copy of the input string if we are going to modify it! */
739  parse = ast_strdupa(data);
740 
741  AST_STANDARD_APP_ARGS(args, parse);
742 
743  if (ast_strlen_zero(args.conf_name)) {
744  ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
745  return -1;
746  }
747 
748  if (strlen(args.conf_name) >= MAX_CONF_NAME) {
749  ast_log(LOG_WARNING, "%s does not accept conference names longer than %d\n", app, MAX_CONF_NAME - 1);
750  return -1;
751  }
752 
753  if (args.argc == 2) {
754  ast_app_parse_options(app_opts, &conference_bridge_user.flags, conference_bridge_user.opt_args, args.options);
755  }
756 
757  /* Look for a conference bridge matching the provided name */
758  if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) {
759  return -1;
760  }
761 
762  /* Keep a copy of volume adjustments so we can restore them later if need be */
763  volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
764  volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
765 
766  /* Always initialize the features structure, we are in most cases always going to need it. */
767  ast_bridge_features_init(&conference_bridge_user.features);
768 
769  /* If the menu option is enabled provide a user or admin menu as a custom feature hook */
770  if (ast_test_flag(&conference_bridge_user.flags, OPTION_MENU)) {
771  ast_bridge_features_hook(&conference_bridge_user.features, "#", menu_callback, &conference_bridge_user);
772  }
773 
774  /* If the caller should be joined already muted, make it so */
775  if (ast_test_flag(&conference_bridge_user.flags, OPTION_STARTMUTED)) {
776  conference_bridge_user.features.mute = 1;
777  }
778 
779  /* Grab join/leave sounds from the channel */
780  ast_channel_lock(chan);
781  if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_JOIN_SOUND"))) {
782  join_sound = ast_strdupa(tmp);
783  }
784  if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_LEAVE_SOUND"))) {
785  leave_sound = ast_strdupa(tmp);
786  }
787  ast_channel_unlock(chan);
788 
789  /* If there is 1 or more people already in the conference then play our join sound unless overridden */
790  if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(join_sound) && conference_bridge->users >= 2) {
791  ast_autoservice_start(chan);
792  play_sound_file(conference_bridge, join_sound);
793  ast_autoservice_stop(chan);
794  }
795 
796  /* Join our conference bridge for real */
797  ast_bridge_join(conference_bridge->bridge, chan, NULL, &conference_bridge_user.features);
798 
799  /* If there is 1 or more people (not including us) already in the conference then play our leave sound unless overridden */
800  if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(leave_sound) && conference_bridge->users >= 2) {
801  ast_autoservice_start(chan);
802  play_sound_file(conference_bridge, leave_sound);
803  ast_autoservice_stop(chan);
804  }
805 
806  /* Easy as pie, depart this channel from the conference bridge */
807  leave_conference_bridge(conference_bridge, &conference_bridge_user);
808  conference_bridge = NULL;
809 
810  /* Can't forget to clean up the features structure, or else we risk a memory leak */
811  ast_bridge_features_cleanup(&conference_bridge_user.features);
812 
813  /* If the user was kicked from the conference play back the audio prompt for it */
814  if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && conference_bridge_user.kicked) {
815  res = ast_stream_and_wait(chan, "conf-kicked", "");
816  }
817 
818  /* Restore volume adjustments to previous values in case they were changed */
819  if (volume_adjustments[0]) {
820  ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
821  }
822  if (volume_adjustments[1]) {
823  ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
824  }
825 
826  return res;
827 }
828 
829 /*! \brief Called when module is being unloaded */
830 static int unload_module(void)
831 {
832  int res = ast_unregister_application(app);
833 
834  /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
836 
837  return res;
838 }
839 
840 /*! \brief Called when module is being loaded */
841 static int load_module(void)
842 {
843  /* Create a container to hold the conference bridges */
846  }
847 
851  }
852 
854 }
855 
856 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Conference Bridge Application",
857  .load = load_module,
858  .unload = unload_module,
859  .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
860 );
int ast_audiohook_volume_set(struct ast_channel *chan, enum ast_audiohook_direction direction, int volume)
Adjust the volume on frames read from or written to a channel.
Definition: audiohook.c:979
int ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2804
#define ast_channel_lock(chan)
Definition: channel.h:2466
Main Channel structure associated with a channel.
Definition: channel.h:742
unsigned int locked
Music on hold handling.
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
Definition: app.h:732
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:946
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
#define ao2_link(arg1, arg2)
Definition: astobj2.h:785
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:420
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
Definition: app.h:712
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:179
The structure that represents a conference bridge.
int ast_audiohook_volume_get(struct ast_channel *chan, enum ast_audiohook_direction direction)
Retrieve the volume adjustment value on frames read from or written to a channel. ...
Definition: audiohook.c:1004
ast_mutex_t playback_lock
enum ast_bridge_channel_state ast_bridge_join(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features)
Join (blocking) a channel to a bridge.
Definition: bridging.c:993
struct ast_channel * playback_chan
#define AST_DIGIT_ANY
Definition: file.h:47
Structure that contains features information.
#define ast_test_flag(p, flag)
Definition: utils.h:63
struct conference_bridge * conference_bridge
#define LOG_WARNING
Definition: logger.h:144
Audiohooks Architecture.
static int announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
Announce number of users in the conference bridge to the caller.
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: app.c:2101
static int load_module(void)
Called when module is being loaded.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Definition: app.h:572
unsigned int flags
Definition: utils.h:201
struct ast_bridge * ast_bridge_new(enum ast_bridge_capability capabilities, int flags)
Create a new bridge.
Definition: bridging.c:449
int ast_bridge_depart(struct ast_bridge *bridge, struct ast_channel *chan)
Depart a channel from a bridge.
Definition: bridging.c:1077
int ast_bridge_features_init(struct ast_bridge_features *features)
Initialize bridge features structure.
Definition: bridging.c:1322
char name[MAX_CONF_NAME]
int ast_bridge_destroy(struct ast_bridge *bridge)
Destroy a bridge.
Definition: bridging.c:515
#define ast_mutex_lock(a)
Definition: lock.h:155
#define ao2_unlock(a)
Definition: astobj2.h:497
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:374
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
static const char app[]
#define CONFERENCE_BRIDGE_BUCKETS
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
Definition: linkedlists.h:841
static int post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
Perform post-joining non-marked specific actions.
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx.c:7705
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:8051
struct ast_bridge * bridge
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
Definition: pbx.c:10475
static void destroy_conference_bridge(void *obj)
Destroy a conference bridge.
int ast_bridge_features_hook(struct ast_bridge_features *features, const char *dtmf, ast_bridge_features_hook_callback callback, void *hook_pvt)
Attach a custom hook to a bridge features structure.
Definition: bridging.c:1274
static int menu_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
DTMF Menu Callback.
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
struct ast_flags flags
unsigned int users
int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt,...)
Tells Asterisk the State for Device is changed.
Definition: devicestate.c:516
int ast_bridge_impart(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features)
Impart (non-blocking) a channel on a bridge.
Definition: bridging.c:1043
General Asterisk PBX channel definitions.
static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
Leave a conference bridge.
struct conference_bridge::@5 users_list
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
Channel Bridging API.
static int play_prompt_to_channel(struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file)
Play back an audio file to a channel.
#define ao2_ref(o, delta)
Definition: astobj2.h:472
#define ao2_lock(a)
Definition: astobj2.h:488
static struct ao2_container * conference_bridges
Container to hold all conference bridges in progress.
int ast_bridge_suspend(struct ast_bridge *bridge, struct ast_channel *chan)
Suspend a channel temporarily from a bridge.
Definition: bridging.c:1200
Core PBX routines and definitions.
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:238
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
Definition: linkedlists.h:224
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: utils.h:663
struct ast_bridge_features features
Structure that contains information about a bridge.
Definition: bridging.h:149
#define LOG_ERROR
Definition: logger.h:155
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:716
The structure that represents a conference bridge user.
unsigned int markedusers
static struct @350 args
int ast_bridge_remove(struct ast_bridge *bridge, struct ast_channel *chan)
Remove a channel from a bridge.
Definition: bridging.c:1100
const ast_string_field name
Definition: channel.h:787
int ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
Turn on music on hold on a given channel.
Definition: channel.c:8040
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename)
Play sound file into conference bridge.
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:430
#define AST_LIST_LAST(head)
Returns the last entry contained in a list.
Definition: linkedlists.h:428
struct ast_bridge * bridge
Definition: channel.h:865
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
#define ao2_find(arg1, arg2, arg3)
Definition: astobj2.h:964
#define ast_channel_unlock(chan)
Definition: channel.h:2467
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1858
int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
stream file until digit If the file name is non-empty, try to play it.
Definition: file.c:1370
struct ast_channel *(*const bridged_channel)(struct ast_channel *chan, struct ast_channel *bridge)
Find bridged channel.
Definition: channel.h:597
static int post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
Perform post-joining marked specific actions.
int ast_call(struct ast_channel *chan, char *addr, int timeout)
Make a call.
Definition: channel.c:5761
int ast_audiohook_volume_adjust(struct ast_channel *chan, enum ast_audiohook_direction direction, int volume)
Adjust the volume on frames read from or written to a channel.
Definition: audiohook.c:1030
Structure used to handle boolean flags.
Definition: utils.h:200
Support for logging to various files, console and syslog Configuration in file logger.conf.
static int unload_module(void)
Called when module is being unloaded.
static int conference_bridge_hash_cb(const void *obj, const int flags)
Hashing function used for conference bridges container.
struct ast_channel * chan
Definition: bridging.h:125
Structure that contains information regarding a channel in a bridge.
Definition: bridging.h:117
#define AST_FORMAT_SLINEAR
Definition: frame.h:254
int ast_say_number(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options)
says a number
Definition: channel.c:8397
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
#define ao2_container_alloc(arg1, arg2, arg3)
Definition: astobj2.h:734
struct conference_bridge_user::@6 list
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1343
static struct conference_bridge * join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user)
Join a conference bridge.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
#define MAX_CONF_NAME
char * opt_args[OPTION_ARRAY_SIZE]
static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
Comparison function used for conference bridges container.
#define AST_APP_ARG(name)
Define an application argument.
Definition: app.h:555
#define ast_mutex_init(pmutex)
Definition: lock.h:152
struct ast_channel * chan
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
Definition: app.h:604
#define ast_mutex_destroy(a)
Definition: lock.h:154
Say numbers and dates (maybe words one day too)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:38
Asterisk module definitions.
enum ast_bridge_channel_state state
Definition: bridging.h:123
struct ast_channel * ast_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *status)
Requests a channel.
Definition: channel.c:5695
struct ast_channel_tech * tech
Definition: channel.h:743
#define ao2_unlink(arg1, arg2)
Definition: astobj2.h:817
int ast_bridge_features_cleanup(struct ast_bridge_features *features)
Clean up the contents of a bridge features structure.
Definition: bridging.c:1333
const ast_string_field language
Definition: channel.h:787
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:128
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:437
int ast_bridge_unsuspend(struct ast_bridge *bridge, struct ast_channel *chan)
Unsuspend a channel from a bridge.
Definition: bridging.c:1218
Structure for mutex and tracking information.
Definition: lock.h:121
static force_inline int attribute_pure ast_str_case_hash(const char *str)
Compute a hash value on a case-insensitive string.
Definition: strings.h:989
static int confbridge_exec(struct ast_channel *chan, const char *data)
The ConfBridge application.
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
Definition: app.h:721
static struct ast_app_option app_opts[128]
#define ast_mutex_unlock(a)
Definition: lock.h:156