app_confbridge.c 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861
  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. /*! \file
  19. *
  20. * \brief Conference Bridge application
  21. *
  22. * \author\verbatim Joshua Colp <jcolp@digium.com> \endverbatim
  23. *
  24. * This is a conference bridge application utilizing the bridging core.
  25. * \ingroup applications
  26. */
  27. /*** MODULEINFO
  28. <support_level>extended</support_level>
  29. ***/
  30. #include "asterisk.h"
  31. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include <unistd.h>
  35. #include <string.h>
  36. #include <signal.h>
  37. #include "asterisk/file.h"
  38. #include "asterisk/logger.h"
  39. #include "asterisk/channel.h"
  40. #include "asterisk/pbx.h"
  41. #include "asterisk/module.h"
  42. #include "asterisk/lock.h"
  43. #include "asterisk/app.h"
  44. #include "asterisk/bridging.h"
  45. #include "asterisk/musiconhold.h"
  46. #include "asterisk/say.h"
  47. #include "asterisk/audiohook.h"
  48. #include "asterisk/astobj2.h"
  49. /*** DOCUMENTATION
  50. <application name="ConfBridge" language="en_US">
  51. <synopsis>
  52. Conference bridge application.
  53. </synopsis>
  54. <syntax>
  55. <parameter name="confno">
  56. <para>The conference number</para>
  57. </parameter>
  58. <parameter name="options">
  59. <optionlist>
  60. <option name="a">
  61. <para>Set admin mode.</para>
  62. </option>
  63. <option name="A">
  64. <para>Set marked mode.</para>
  65. </option>
  66. <option name="c">
  67. <para>Announce user(s) count on joining a conference.</para>
  68. </option>
  69. <option name="m">
  70. <para>Set initially muted.</para>
  71. </option>
  72. <option name="M" hasparams="optional">
  73. <para>Enable music on hold when the conference has a single caller. Optionally,
  74. specify a musiconhold class to use. If one is not provided, it will use the
  75. channel's currently set music class, or <literal>default</literal>.</para>
  76. <argument name="class" required="true" />
  77. </option>
  78. <option name="1">
  79. <para>Do not play message when first person enters</para>
  80. </option>
  81. <option name="s">
  82. <para>Present menu (user or admin) when <literal>#</literal> is received
  83. (send to menu).</para>
  84. </option>
  85. <option name="w">
  86. <para>Wait until the marked user enters the conference.</para>
  87. </option>
  88. <option name="q">
  89. <para>Quiet mode (don't play enter/leave sounds).</para>
  90. </option>
  91. </optionlist>
  92. </parameter>
  93. </syntax>
  94. <description>
  95. <para>Enters the user into a specified conference bridge. The user can exit the conference by hangup only.</para>
  96. <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>
  97. <note><para>This application will not automatically answer the channel.</para></note>
  98. </description>
  99. </application>
  100. ***/
  101. /*!
  102. * \par Playing back a file to a channel in a conference
  103. * You might notice in this application that while playing a sound file
  104. * to a channel the actual conference bridge lock is not held. This is done so
  105. * that other channels are not blocked from interacting with the conference bridge.
  106. * Unfortunately because of this it is possible for things to change after the sound file
  107. * is done being played. Data must therefore be checked after reacquiring the conference
  108. * bridge lock if it is important.
  109. */
  110. static const char app[] = "ConfBridge";
  111. enum {
  112. OPTION_ADMIN = (1 << 0), /*!< Set if the caller is an administrator */
  113. OPTION_MENU = (1 << 1), /*!< Set if the caller should have access to the conference bridge IVR menu */
  114. OPTION_MUSICONHOLD = (1 << 2), /*!< Set if music on hold should be played if nobody else is in the conference bridge */
  115. OPTION_NOONLYPERSON = (1 << 3), /*!< Set if the "you are currently the only person in this conference" sound file should not be played */
  116. OPTION_STARTMUTED = (1 << 4), /*!< Set if the caller should be initially set muted */
  117. OPTION_ANNOUNCEUSERCOUNT = (1 << 5), /*!< Set if the number of users should be announced to the caller */
  118. OPTION_MARKEDUSER = (1 << 6), /*!< Set if the caller is a marked user */
  119. OPTION_WAITMARKED = (1 << 7), /*!< Set if the conference must wait for a marked user before starting */
  120. OPTION_QUIET = (1 << 8), /*!< Set if no audio prompts should be played */
  121. };
  122. enum {
  123. OPTION_MUSICONHOLD_CLASS, /*!< If the 'M' option is set, the music on hold class to play */
  124. /*This must be the last element */
  125. OPTION_ARRAY_SIZE,
  126. };
  127. AST_APP_OPTIONS(app_opts,{
  128. AST_APP_OPTION('A', OPTION_MARKEDUSER),
  129. AST_APP_OPTION('a', OPTION_ADMIN),
  130. AST_APP_OPTION('c', OPTION_ANNOUNCEUSERCOUNT),
  131. AST_APP_OPTION('m', OPTION_STARTMUTED),
  132. AST_APP_OPTION_ARG('M', OPTION_MUSICONHOLD, OPTION_MUSICONHOLD_CLASS),
  133. AST_APP_OPTION('1', OPTION_NOONLYPERSON),
  134. AST_APP_OPTION('s', OPTION_MENU),
  135. AST_APP_OPTION('w', OPTION_WAITMARKED),
  136. AST_APP_OPTION('q', OPTION_QUIET),
  137. });
  138. /* Maximum length of a conference bridge name */
  139. #define MAX_CONF_NAME AST_MAX_EXTENSION
  140. /* Number of buckets our conference bridges container can have */
  141. #define CONFERENCE_BRIDGE_BUCKETS 53
  142. /*! \brief The structure that represents a conference bridge */
  143. struct conference_bridge {
  144. char name[MAX_CONF_NAME]; /*!< Name of the conference bridge */
  145. struct ast_bridge *bridge; /*!< Bridge structure doing the mixing */
  146. unsigned int users; /*!< Number of users present */
  147. unsigned int markedusers; /*!< Number of marked users present */
  148. unsigned int locked:1; /*!< Is this conference bridge locked? */
  149. AST_LIST_HEAD_NOLOCK(, conference_bridge_user) users_list; /*!< List of users participating in the conference bridge */
  150. struct ast_channel *playback_chan; /*!< Channel used for playback into the conference bridge */
  151. ast_mutex_t playback_lock; /*!< Lock used for playback channel */
  152. };
  153. /*! \brief The structure that represents a conference bridge user */
  154. struct conference_bridge_user {
  155. struct conference_bridge *conference_bridge; /*!< Conference bridge they are participating in */
  156. struct ast_channel *chan; /*!< Asterisk channel participating */
  157. struct ast_flags flags; /*!< Flags passed in when the application was called */
  158. char *opt_args[OPTION_ARRAY_SIZE]; /*!< Arguments to options passed when application was called */
  159. struct ast_bridge_features features; /*!< Bridge features structure */
  160. unsigned int kicked:1; /*!< User has been kicked from the conference */
  161. AST_LIST_ENTRY(conference_bridge_user) list; /*!< Linked list information */
  162. };
  163. /*! \brief Container to hold all conference bridges in progress */
  164. static struct ao2_container *conference_bridges;
  165. static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename);
  166. /*! \brief Hashing function used for conference bridges container */
  167. static int conference_bridge_hash_cb(const void *obj, const int flags)
  168. {
  169. const struct conference_bridge *conference_bridge = obj;
  170. return ast_str_case_hash(conference_bridge->name);
  171. }
  172. /*! \brief Comparison function used for conference bridges container */
  173. static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
  174. {
  175. const struct conference_bridge *conference_bridge0 = obj, *conference_bridge1 = arg;
  176. return (!strcasecmp(conference_bridge0->name, conference_bridge1->name) ? CMP_MATCH | CMP_STOP : 0);
  177. }
  178. /*!
  179. * \brief Announce number of users in the conference bridge to the caller
  180. *
  181. * \param conference_bridge Conference bridge to peek at
  182. * \param conference_bridge_user Caller
  183. *
  184. * \return Returns 0 on success, -1 if the user hung up
  185. */
  186. static int announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
  187. {
  188. if (conference_bridge->users == 1) {
  189. /* Awww we are the only person in the conference bridge */
  190. return 0;
  191. } else if (conference_bridge->users == 2) {
  192. /* Eep, there is one other person */
  193. if (ast_stream_and_wait(conference_bridge_user->chan, "conf-onlyone", "")) {
  194. return -1;
  195. }
  196. } else {
  197. /* Alas multiple others in here */
  198. if (ast_stream_and_wait(conference_bridge_user->chan, "conf-thereare", "")) {
  199. return -1;
  200. }
  201. if (ast_say_number(conference_bridge_user->chan, conference_bridge->users - 1, "", conference_bridge_user->chan->language, NULL)) {
  202. return -1;
  203. }
  204. if (ast_stream_and_wait(conference_bridge_user->chan, "conf-otherinparty", "")) {
  205. return -1;
  206. }
  207. }
  208. return 0;
  209. }
  210. /*!
  211. * \brief Play back an audio file to a channel
  212. *
  213. * \param conference_bridge Conference bridge they are in
  214. * \param chan Channel to play audio prompt to
  215. * \param file Prompt to play
  216. *
  217. * \return Returns 0 on success, -1 if the user hung up
  218. *
  219. * \note This function assumes that conference_bridge is locked
  220. */
  221. static int play_prompt_to_channel(struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file)
  222. {
  223. int res;
  224. ao2_unlock(conference_bridge);
  225. res = ast_stream_and_wait(chan, file, "");
  226. ao2_lock(conference_bridge);
  227. return res;
  228. }
  229. /*!
  230. * \brief Perform post-joining marked specific actions
  231. *
  232. * \param conference_bridge Conference bridge being joined
  233. * \param conference_bridge_user Conference bridge user joining
  234. *
  235. * \return Returns 0 on success, -1 if the user hung up
  236. */
  237. static int post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
  238. {
  239. if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
  240. struct conference_bridge_user *other_conference_bridge_user = NULL;
  241. /* If we are not the first marked user to join just bail out now */
  242. if (conference_bridge->markedusers >= 2) {
  243. return 0;
  244. }
  245. /* Iterate through every participant stopping MOH on them if need be */
  246. AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
  247. if (other_conference_bridge_user == conference_bridge_user) {
  248. continue;
  249. }
  250. if (ast_test_flag(&other_conference_bridge_user->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan)) {
  251. ast_moh_stop(other_conference_bridge_user->chan);
  252. ast_bridge_unsuspend(conference_bridge->bridge, other_conference_bridge_user->chan);
  253. }
  254. }
  255. /* Next play the audio file stating they are going to be placed into the conference */
  256. if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
  257. if (play_prompt_to_channel(conference_bridge,
  258. conference_bridge_user->chan,
  259. "conf-placeintoconf")) {
  260. /* user hungup while the sound was playing */
  261. return -1;
  262. }
  263. }
  264. /* Finally iterate through and unmute them all */
  265. AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
  266. if (other_conference_bridge_user == conference_bridge_user) {
  267. continue;
  268. }
  269. other_conference_bridge_user->features.mute = 0;
  270. }
  271. } else {
  272. /* If a marked user already exists in the conference bridge we can just bail out now */
  273. if (conference_bridge->markedusers) {
  274. return 0;
  275. }
  276. /* Be sure we are muted so we can't talk to anybody else waiting */
  277. conference_bridge_user->features.mute = 1;
  278. /* If we have not been quieted play back that they are waiting for the leader */
  279. if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
  280. if (play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-waitforleader")) {
  281. /* user hung up while the sound was playing */
  282. return -1;
  283. }
  284. }
  285. /* Start music on hold if needed */
  286. /* We need to recheck the markedusers value here. play_prompt_to_channel unlocks the conference bridge, potentially
  287. * allowing a marked user to enter while the prompt was playing
  288. */
  289. if (!conference_bridge->markedusers && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
  290. ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
  291. }
  292. }
  293. return 0;
  294. }
  295. /*!
  296. * \brief Perform post-joining non-marked specific actions
  297. *
  298. * \param conference_bridge Conference bridge being joined
  299. * \param conference_bridge_user Conference bridge user joining
  300. *
  301. * \return Returns 0 on success, -1 if the user hung up
  302. */
  303. static int post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
  304. {
  305. /* Play back audio prompt and start MOH if need be if we are the first participant */
  306. if (conference_bridge->users == 1) {
  307. /* If audio prompts have not been quieted or this prompt quieted play it on out */
  308. if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET | OPTION_NOONLYPERSON)) {
  309. if (play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-onlyperson")) {
  310. /* user hung up while the sound was playing */
  311. return -1;
  312. }
  313. }
  314. /* If we need to start music on hold on the channel do so now */
  315. /* We need to re-check the number of users in the conference bridge here because another conference bridge
  316. * participant could have joined while the above prompt was playing for the first user.
  317. */
  318. if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
  319. ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
  320. }
  321. return 0;
  322. }
  323. /* Announce number of users if need be */
  324. if (ast_test_flag(&conference_bridge_user->flags, OPTION_ANNOUNCEUSERCOUNT)) {
  325. ao2_unlock(conference_bridge);
  326. if (announce_user_count(conference_bridge, conference_bridge_user)) {
  327. ao2_lock(conference_bridge);
  328. return -1;
  329. }
  330. ao2_lock(conference_bridge);
  331. }
  332. /* If we are the second participant we may need to stop music on hold on the first */
  333. if (conference_bridge->users == 2) {
  334. struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
  335. /* Temporarily suspend the above participant from the bridge so we have control to stop MOH if needed */
  336. if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
  337. ast_moh_stop(first_participant->chan);
  338. ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
  339. }
  340. }
  341. return 0;
  342. }
  343. /*!
  344. * \brief Destroy a conference bridge
  345. *
  346. * \param obj The conference bridge object
  347. *
  348. * \return Returns nothing
  349. */
  350. static void destroy_conference_bridge(void *obj)
  351. {
  352. struct conference_bridge *conference_bridge = obj;
  353. ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name);
  354. ast_mutex_destroy(&conference_bridge->playback_lock);
  355. if (conference_bridge->playback_chan) {
  356. struct ast_channel *underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
  357. ast_hangup(underlying_channel);
  358. ast_hangup(conference_bridge->playback_chan);
  359. conference_bridge->playback_chan = NULL;
  360. }
  361. /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
  362. if (conference_bridge->bridge) {
  363. ast_bridge_destroy(conference_bridge->bridge);
  364. conference_bridge->bridge = NULL;
  365. }
  366. }
  367. static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user);
  368. /*!
  369. * \brief Join a conference bridge
  370. *
  371. * \param name The conference name
  372. * \param conference_bridge_user Conference bridge user structure
  373. *
  374. * \return A pointer to the conference bridge struct, or NULL if the conference room wasn't found.
  375. */
  376. static struct conference_bridge *join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user)
  377. {
  378. struct conference_bridge *conference_bridge = NULL;
  379. struct conference_bridge tmp;
  380. ast_copy_string(tmp.name, name, sizeof(tmp.name));
  381. /* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */
  382. ao2_lock(conference_bridges);
  383. ast_debug(1, "Trying to find conference bridge '%s'\n", name);
  384. /* Attempt to find an existing conference bridge */
  385. conference_bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
  386. /* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */
  387. if (conference_bridge && conference_bridge->locked && !ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN)) {
  388. ao2_unlock(conference_bridges);
  389. ao2_ref(conference_bridge, -1);
  390. ast_debug(1, "Conference bridge '%s' is locked and caller is not an admin\n", name);
  391. ast_stream_and_wait(conference_bridge_user->chan, "conf-locked", "");
  392. return NULL;
  393. }
  394. /* If no conference bridge was found see if we can create one */
  395. if (!conference_bridge) {
  396. /* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */
  397. if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) {
  398. ao2_unlock(conference_bridges);
  399. ast_log(LOG_ERROR, "Conference bridge '%s' does not exist.\n", name);
  400. return NULL;
  401. }
  402. /* Setup conference bridge parameters */
  403. ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
  404. /* Create an actual bridge that will do the audio mixing */
  405. if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_SMART))) {
  406. ao2_ref(conference_bridge, -1);
  407. conference_bridge = NULL;
  408. ao2_unlock(conference_bridges);
  409. ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name);
  410. return NULL;
  411. }
  412. /* Setup lock for playback channel */
  413. ast_mutex_init(&conference_bridge->playback_lock);
  414. /* Link it into the conference bridges container */
  415. ao2_link(conference_bridges, conference_bridge);
  416. ast_debug(1, "Created conference bridge '%s' and linked to container '%p'\n", name, conference_bridges);
  417. }
  418. ao2_unlock(conference_bridges);
  419. /* Setup conference bridge user parameters */
  420. conference_bridge_user->conference_bridge = conference_bridge;
  421. ao2_lock(conference_bridge);
  422. /* All good to go, add them in */
  423. AST_LIST_INSERT_TAIL(&conference_bridge->users_list, conference_bridge_user, list);
  424. /* Increment the users count on the bridge, but record it as it is going to need to be known right after this */
  425. conference_bridge->users++;
  426. /* If the caller is a marked user bump up the count */
  427. if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
  428. conference_bridge->markedusers++;
  429. }
  430. /* Set the device state for this conference */
  431. if (conference_bridge->users == 1) {
  432. ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "confbridge:%s", conference_bridge->name);
  433. }
  434. /* 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 */
  435. if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER | OPTION_WAITMARKED)) {
  436. if (post_join_marked(conference_bridge, conference_bridge_user)) {
  437. ao2_unlock(conference_bridge);
  438. leave_conference_bridge(conference_bridge, conference_bridge_user);
  439. return NULL;
  440. }
  441. } else {
  442. if (post_join_unmarked(conference_bridge, conference_bridge_user)) {
  443. ao2_unlock(conference_bridge);
  444. leave_conference_bridge(conference_bridge, conference_bridge_user);
  445. return NULL;
  446. }
  447. }
  448. ao2_unlock(conference_bridge);
  449. return conference_bridge;
  450. }
  451. /*!
  452. * \brief Leave a conference bridge
  453. *
  454. * \param conference_bridge The conference bridge to leave
  455. * \param conference_bridge_user The conference bridge user structure
  456. *
  457. */
  458. static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
  459. {
  460. ao2_lock(conference_bridge);
  461. /* If this caller is a marked user bump down the count */
  462. if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
  463. conference_bridge->markedusers--;
  464. }
  465. /* Decrement the users count while keeping the previous participant count */
  466. conference_bridge->users--;
  467. /* Drop conference bridge user from the list, they be going bye bye */
  468. AST_LIST_REMOVE(&conference_bridge->users_list, conference_bridge_user, list);
  469. /* If there are still users in the conference bridge we may need to do things (such as start MOH on them) */
  470. if (conference_bridge->users) {
  471. if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER) && !conference_bridge->markedusers) {
  472. struct conference_bridge_user *other_participant = NULL;
  473. /* Start out with muting everyone */
  474. AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
  475. other_participant->features.mute = 1;
  476. }
  477. /* Play back the audio prompt saying the leader has left the conference */
  478. if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
  479. ao2_unlock(conference_bridge);
  480. ast_autoservice_start(conference_bridge_user->chan);
  481. play_sound_file(conference_bridge, "conf-leaderhasleft");
  482. ast_autoservice_stop(conference_bridge_user->chan);
  483. ao2_lock(conference_bridge);
  484. }
  485. /* Now on to starting MOH if needed */
  486. AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
  487. if (ast_test_flag(&other_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) {
  488. ast_moh_start(other_participant->chan, other_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
  489. ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan);
  490. }
  491. }
  492. } else if (conference_bridge->users == 1) {
  493. /* Of course if there is one other person in here we may need to start up MOH on them */
  494. struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
  495. if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
  496. ast_moh_start(first_participant->chan, first_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
  497. ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
  498. }
  499. }
  500. } else {
  501. /* Set device state to "not in use" */
  502. ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "confbridge:%s", conference_bridge->name);
  503. ao2_unlink(conference_bridges, conference_bridge);
  504. }
  505. /* Done mucking with the conference bridge, huzzah */
  506. ao2_unlock(conference_bridge);
  507. ao2_ref(conference_bridge, -1);
  508. }
  509. /*!
  510. * \brief Play sound file into conference bridge
  511. *
  512. * \param conference_bridge The conference bridge to play sound file into
  513. * \param filename Sound file to play
  514. *
  515. * \retval 0 success
  516. * \retval -1 failure
  517. */
  518. static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename)
  519. {
  520. struct ast_channel *underlying_channel;
  521. ast_mutex_lock(&conference_bridge->playback_lock);
  522. if (!(conference_bridge->playback_chan)) {
  523. int cause;
  524. if (!(conference_bridge->playback_chan = ast_request("Bridge", AST_FORMAT_SLINEAR, NULL, "", &cause))) {
  525. ast_mutex_unlock(&conference_bridge->playback_lock);
  526. return -1;
  527. }
  528. conference_bridge->playback_chan->bridge = conference_bridge->bridge;
  529. if (ast_call(conference_bridge->playback_chan, "", 0)) {
  530. ast_hangup(conference_bridge->playback_chan);
  531. conference_bridge->playback_chan = NULL;
  532. ast_mutex_unlock(&conference_bridge->playback_lock);
  533. return -1;
  534. }
  535. ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name);
  536. underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
  537. } else {
  538. /* Channel was already available so we just need to add it back into the bridge */
  539. underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
  540. ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL);
  541. }
  542. /* The channel is all under our control, in goes the prompt */
  543. ast_stream_and_wait(conference_bridge->playback_chan, filename, "");
  544. ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", underlying_channel->name, conference_bridge->bridge);
  545. ast_bridge_depart(conference_bridge->bridge, underlying_channel);
  546. ast_mutex_unlock(&conference_bridge->playback_lock);
  547. return 0;
  548. }
  549. /*!
  550. * \brief DTMF Menu Callback
  551. *
  552. * \param bridge Bridge this is involving
  553. * \param bridge_channel Bridged channel this is involving
  554. * \param hook_pvt User's conference bridge structure
  555. *
  556. * \retval 0 success
  557. * \retval -1 failure
  558. */
  559. static int menu_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
  560. {
  561. struct conference_bridge_user *conference_bridge_user = hook_pvt;
  562. struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge;
  563. int digit, res = 0, isadmin = ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN);
  564. /* See if music on hold is playing */
  565. ao2_lock(conference_bridge);
  566. if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
  567. /* Just us so MOH is probably indeed going, let's stop it */
  568. ast_moh_stop(bridge_channel->chan);
  569. }
  570. ao2_unlock(conference_bridge);
  571. /* Try to play back the user menu, if it fails pass this back up so the bridging core will act on it */
  572. if (ast_streamfile(bridge_channel->chan, (isadmin ? "conf-adminmenu" : "conf-usermenu"), bridge_channel->chan->language)) {
  573. res = -1;
  574. goto finished;
  575. }
  576. /* Wait for them to enter a digit from the user menu options */
  577. digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY);
  578. ast_stopstream(bridge_channel->chan);
  579. if (digit == '1') {
  580. /* 1 - Mute or unmute yourself, note we only allow manipulation if they aren't waiting for a marked user or if marked users exist */
  581. if (!ast_test_flag(&conference_bridge_user->flags, OPTION_WAITMARKED) || conference_bridge->markedusers) {
  582. conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0);
  583. }
  584. res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge_user->features.mute ? "conf-muted" : "conf-unmuted"), "");
  585. } else if (isadmin && digit == '2') {
  586. /* 2 - Unlock or lock conference */
  587. conference_bridge->locked = (!conference_bridge->locked ? 1 : 0);
  588. res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge->locked ? "conf-lockednow" : "conf-unlockednow"), "");
  589. } else if (isadmin && digit == '3') {
  590. /* 3 - Eject last user */
  591. struct conference_bridge_user *last_participant = NULL;
  592. ao2_lock(conference_bridge);
  593. if (((last_participant = AST_LIST_LAST(&conference_bridge->users_list)) == conference_bridge_user) || (ast_test_flag(&last_participant->flags, OPTION_ADMIN))) {
  594. ao2_unlock(conference_bridge);
  595. res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
  596. } else {
  597. last_participant->kicked = 1;
  598. ast_bridge_remove(conference_bridge->bridge, last_participant->chan);
  599. ao2_unlock(conference_bridge);
  600. }
  601. } else if (digit == '4') {
  602. /* 4 - Decrease listening volume */
  603. ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, -1);
  604. } else if (digit == '6') {
  605. /* 6 - Increase listening volume */
  606. ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 1);
  607. } else if (digit == '7') {
  608. /* 7 - Decrease talking volume */
  609. ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, -1);
  610. } else if (digit == '8') {
  611. /* 8 - Exit the IVR */
  612. } else if (digit == '9') {
  613. /* 9 - Increase talking volume */
  614. ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 1);
  615. } else {
  616. /* No valid option was selected */
  617. res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
  618. }
  619. finished:
  620. /* See if music on hold needs to be started back up again */
  621. ao2_lock(conference_bridge);
  622. if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
  623. ast_moh_start(bridge_channel->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
  624. }
  625. ao2_unlock(conference_bridge);
  626. bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
  627. return res;
  628. }
  629. /*! \brief The ConfBridge application */
  630. static int confbridge_exec(struct ast_channel *chan, const char *data)
  631. {
  632. int res = 0, volume_adjustments[2];
  633. char *parse;
  634. struct conference_bridge *conference_bridge = NULL;
  635. struct conference_bridge_user conference_bridge_user = {
  636. .chan = chan,
  637. };
  638. const char *tmp, *join_sound = NULL, *leave_sound = NULL;
  639. AST_DECLARE_APP_ARGS(args,
  640. AST_APP_ARG(conf_name);
  641. AST_APP_ARG(options);
  642. );
  643. /* We need to make a copy of the input string if we are going to modify it! */
  644. parse = ast_strdupa(data);
  645. AST_STANDARD_APP_ARGS(args, parse);
  646. if (ast_strlen_zero(args.conf_name)) {
  647. ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
  648. return -1;
  649. }
  650. if (strlen(args.conf_name) >= MAX_CONF_NAME) {
  651. ast_log(LOG_WARNING, "%s does not accept conference names longer than %d\n", app, MAX_CONF_NAME - 1);
  652. return -1;
  653. }
  654. if (args.argc == 2) {
  655. ast_app_parse_options(app_opts, &conference_bridge_user.flags, conference_bridge_user.opt_args, args.options);
  656. }
  657. /* Look for a conference bridge matching the provided name */
  658. if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) {
  659. return -1;
  660. }
  661. /* Keep a copy of volume adjustments so we can restore them later if need be */
  662. volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
  663. volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
  664. /* Always initialize the features structure, we are in most cases always going to need it. */
  665. ast_bridge_features_init(&conference_bridge_user.features);
  666. /* If the menu option is enabled provide a user or admin menu as a custom feature hook */
  667. if (ast_test_flag(&conference_bridge_user.flags, OPTION_MENU)) {
  668. ast_bridge_features_hook(&conference_bridge_user.features, "#", menu_callback, &conference_bridge_user);
  669. }
  670. /* If the caller should be joined already muted, make it so */
  671. if (ast_test_flag(&conference_bridge_user.flags, OPTION_STARTMUTED)) {
  672. conference_bridge_user.features.mute = 1;
  673. }
  674. /* Grab join/leave sounds from the channel */
  675. ast_channel_lock(chan);
  676. if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_JOIN_SOUND"))) {
  677. join_sound = ast_strdupa(tmp);
  678. }
  679. if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_LEAVE_SOUND"))) {
  680. leave_sound = ast_strdupa(tmp);
  681. }
  682. ast_channel_unlock(chan);
  683. /* If there is 1 or more people already in the conference then play our join sound unless overridden */
  684. if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(join_sound) && conference_bridge->users >= 2) {
  685. ast_autoservice_start(chan);
  686. play_sound_file(conference_bridge, join_sound);
  687. ast_autoservice_stop(chan);
  688. }
  689. /* Join our conference bridge for real */
  690. ast_bridge_join(conference_bridge->bridge, chan, NULL, &conference_bridge_user.features);
  691. /* If there is 1 or more people (not including us) already in the conference then play our leave sound unless overridden */
  692. if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(leave_sound) && conference_bridge->users >= 2) {
  693. ast_autoservice_start(chan);
  694. play_sound_file(conference_bridge, leave_sound);
  695. ast_autoservice_stop(chan);
  696. }
  697. /* Easy as pie, depart this channel from the conference bridge */
  698. leave_conference_bridge(conference_bridge, &conference_bridge_user);
  699. conference_bridge = NULL;
  700. /* Can't forget to clean up the features structure, or else we risk a memory leak */
  701. ast_bridge_features_cleanup(&conference_bridge_user.features);
  702. /* If the user was kicked from the conference play back the audio prompt for it */
  703. if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && conference_bridge_user.kicked) {
  704. res = ast_stream_and_wait(chan, "conf-kicked", "");
  705. }
  706. /* Restore volume adjustments to previous values in case they were changed */
  707. if (volume_adjustments[0]) {
  708. ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
  709. }
  710. if (volume_adjustments[1]) {
  711. ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
  712. }
  713. return res;
  714. }
  715. /*! \brief Called when module is being unloaded */
  716. static int unload_module(void)
  717. {
  718. int res = ast_unregister_application(app);
  719. /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
  720. ao2_ref(conference_bridges, -1);
  721. return res;
  722. }
  723. /*! \brief Called when module is being loaded */
  724. static int load_module(void)
  725. {
  726. /* Create a container to hold the conference bridges */
  727. if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
  728. return AST_MODULE_LOAD_DECLINE;
  729. }
  730. if (ast_register_application_xml(app, confbridge_exec)) {
  731. ao2_ref(conference_bridges, -1);
  732. return AST_MODULE_LOAD_DECLINE;
  733. }
  734. return AST_MODULE_LOAD_SUCCESS;
  735. }
  736. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Conference Bridge Application",
  737. .load = load_module,
  738. .unload = unload_module,
  739. .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
  740. );