Fri Jul 15 2011 11:56:57

Asterisk developer's documentation


app_confbridge.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2007-2008, Digium, Inc.
00005  *
00006  * Joshua Colp <jcolp@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Conference Bridge application
00022  *
00023  * \author\verbatim Joshua Colp <jcolp@digium.com> \endverbatim
00024  *
00025  * This is a conference bridge application utilizing the bridging core.
00026  * \ingroup applications
00027  */
00028 
00029 #include "asterisk.h"
00030 
00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 324306 $")
00032 
00033 #include <stdio.h>
00034 #include <stdlib.h>
00035 #include <unistd.h>
00036 #include <string.h>
00037 #include <signal.h>
00038 
00039 #include "asterisk/file.h"
00040 #include "asterisk/logger.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/pbx.h"
00043 #include "asterisk/module.h"
00044 #include "asterisk/lock.h"
00045 #include "asterisk/app.h"
00046 #include "asterisk/bridging.h"
00047 #include "asterisk/musiconhold.h"
00048 #include "asterisk/say.h"
00049 #include "asterisk/audiohook.h"
00050 #include "asterisk/astobj2.h"
00051 
00052 /*** DOCUMENTATION
00053         <application name="ConfBridge" language="en_US">
00054                 <synopsis>
00055                         Conference bridge application.
00056                 </synopsis>
00057                 <syntax>
00058                         <parameter name="confno">
00059                                 <para>The conference number</para>
00060                         </parameter>
00061                         <parameter name="options">
00062                                 <optionlist>
00063                                         <option name="a">
00064                                                 <para>Set admin mode.</para>
00065                                         </option>
00066                                         <option name="A">
00067                                                 <para>Set marked mode.</para>
00068                                         </option>
00069                                         <option name="c">
00070                                                 <para>Announce user(s) count on joining a conference.</para>
00071                                         </option>
00072                                         <option name="m">
00073                                                 <para>Set initially muted.</para>
00074                                         </option>
00075                                         <option name="M" hasparams="optional">
00076                                                 <para>Enable music on hold when the conference has a single caller. Optionally,
00077                                                 specify a musiconhold class to use. If one is not provided, it will use the
00078                                                 channel's currently set music class, or <literal>default</literal>.</para>
00079                                                 <argument name="class" required="true" />
00080                                         </option>
00081                                         <option name="1">
00082                                                 <para>Do not play message when first person enters</para>
00083                                         </option>
00084                                         <option name="s">
00085                                                 <para>Present menu (user or admin) when <literal>*</literal> is received
00086                                                 (send to menu).</para>
00087                                         </option>
00088                                         <option name="w">
00089                                                 <para>Wait until the marked user enters the conference.</para>
00090                                         </option>
00091                                         <option name="q">
00092                                                 <para>Quiet mode (don't play enter/leave sounds).</para>
00093                                         </option>
00094             </optionlist>
00095             </parameter>
00096                 </syntax>
00097                 <description>
00098                         <para>Enters the user into a specified conference bridge. The user can exit the conference by hangup only.</para>
00099                         <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>
00100          <note><para>This application will not automatically answer the channel.</para></note>
00101                 </description>
00102         </application>
00103 ***/
00104 
00105 /*!
00106  * \par Playing back a file to a channel in a conference
00107  * You might notice in this application that while playing a sound file
00108  * to a channel the actual conference bridge lock is not held. This is done so
00109  * that other channels are not blocked from interacting with the conference bridge.
00110  * Unfortunately because of this it is possible for things to change after the sound file
00111  * is done being played. Data must therefore be checked after reacquiring the conference
00112  * bridge lock if it is important.
00113  */
00114 
00115 static const char *app = "ConfBridge";
00116 
00117 enum {
00118    OPTION_ADMIN = (1 << 0),             /*!< Set if the caller is an administrator */
00119    OPTION_MENU = (1 << 1),              /*!< Set if the caller should have access to the conference bridge IVR menu */
00120    OPTION_MUSICONHOLD = (1 << 2),       /*!< Set if music on hold should be played if nobody else is in the conference bridge */
00121    OPTION_NOONLYPERSON = (1 << 3),      /*!< Set if the "you are currently the only person in this conference" sound file should not be played */
00122    OPTION_STARTMUTED = (1 << 4),        /*!< Set if the caller should be initially set muted */
00123    OPTION_ANNOUNCEUSERCOUNT = (1 << 5), /*!< Set if the number of users should be announced to the caller */
00124    OPTION_MARKEDUSER = (1 << 6),        /*!< Set if the caller is a marked user */
00125    OPTION_WAITMARKED = (1 << 7),        /*!< Set if the conference must wait for a marked user before starting */
00126    OPTION_QUIET = (1 << 8),             /*!< Set if no audio prompts should be played */
00127 };
00128 
00129 enum {
00130    OPTION_MUSICONHOLD_CLASS,            /*!< If the 'M' option is set, the music on hold class to play */
00131    /*This must be the last element */
00132    OPTION_ARRAY_SIZE,
00133 };
00134 
00135 AST_APP_OPTIONS(app_opts,{
00136    AST_APP_OPTION('A', OPTION_MARKEDUSER),
00137    AST_APP_OPTION('a', OPTION_ADMIN),
00138    AST_APP_OPTION('c', OPTION_ANNOUNCEUSERCOUNT),
00139    AST_APP_OPTION('m', OPTION_STARTMUTED),
00140    AST_APP_OPTION_ARG('M', OPTION_MUSICONHOLD, OPTION_MUSICONHOLD_CLASS),
00141    AST_APP_OPTION('1', OPTION_NOONLYPERSON),
00142    AST_APP_OPTION('s', OPTION_MENU),
00143    AST_APP_OPTION('w', OPTION_WAITMARKED),
00144    AST_APP_OPTION('q', OPTION_QUIET),
00145 });
00146 
00147 /* Maximum length of a conference bridge name */
00148 #define MAX_CONF_NAME 32
00149 
00150 /* Number of buckets our conference bridges container can have */
00151 #define CONFERENCE_BRIDGE_BUCKETS 53
00152 
00153 /*! \brief The structure that represents a conference bridge */
00154 struct conference_bridge {
00155    char name[MAX_CONF_NAME];                                         /*!< Name of the conference bridge */
00156    struct ast_bridge *bridge;                                        /*!< Bridge structure doing the mixing */
00157    unsigned int users;                                               /*!< Number of users present */
00158    unsigned int markedusers;                                         /*!< Number of marked users present */
00159    unsigned int locked:1;                                            /*!< Is this conference bridge locked? */
00160    AST_LIST_HEAD_NOLOCK(, conference_bridge_user) users_list;        /*!< List of users participating in the conference bridge */
00161    struct ast_channel *playback_chan;                                /*!< Channel used for playback into the conference bridge */
00162    ast_mutex_t playback_lock;                                        /*!< Lock used for playback channel */
00163 };
00164 
00165 /*! \brief The structure that represents a conference bridge user */
00166 struct conference_bridge_user {
00167    struct conference_bridge *conference_bridge; /*!< Conference bridge they are participating in */
00168    struct ast_channel *chan;                    /*!< Asterisk channel participating */
00169    struct ast_flags flags;                      /*!< Flags passed in when the application was called */
00170    char *opt_args[OPTION_ARRAY_SIZE];           /*!< Arguments to options passed when application was called */
00171    struct ast_bridge_features features;         /*!< Bridge features structure */
00172    unsigned int kicked:1;                       /*!< User has been kicked from the conference */
00173    AST_LIST_ENTRY(conference_bridge_user) list; /*!< Linked list information */
00174 };
00175 
00176 /*! \brief Container to hold all conference bridges in progress */
00177 static struct ao2_container *conference_bridges;
00178 
00179 static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename);
00180 
00181 /*! \brief Hashing function used for conference bridges container */
00182 static int conference_bridge_hash_cb(const void *obj, const int flags)
00183 {
00184    const struct conference_bridge *conference_bridge = obj;
00185    return ast_str_case_hash(conference_bridge->name);
00186 }
00187 
00188 /*! \brief Comparison function used for conference bridges container */
00189 static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
00190 {
00191    const struct conference_bridge *conference_bridge0 = obj, *conference_bridge1 = arg;
00192    return (!strcasecmp(conference_bridge0->name, conference_bridge1->name) ? CMP_MATCH | CMP_STOP : 0);
00193 }
00194 
00195 /*!
00196  * \brief Announce number of users in the conference bridge to the caller
00197  *
00198  * \param conference_bridge Conference bridge to peek at
00199  * \param conference_bridge_user Caller
00200  *
00201  * \return Returns 0 on success, -1 if the user hung up
00202  */
00203 static int announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
00204 {
00205    if (conference_bridge->users == 1) {
00206       /* Awww we are the only person in the conference bridge */
00207       return 0;
00208    } else if (conference_bridge->users == 2) {
00209       /* Eep, there is one other person */
00210       if (ast_stream_and_wait(conference_bridge_user->chan, "conf-onlyone", "")) {
00211          return -1;
00212       }
00213    } else {
00214       /* Alas multiple others in here */
00215       if (ast_stream_and_wait(conference_bridge_user->chan, "conf-thereare", "")) {
00216          return -1;
00217       }
00218       if (ast_say_number(conference_bridge_user->chan, conference_bridge->users - 1, "", conference_bridge_user->chan->language, NULL)) {
00219          return -1;
00220       }
00221       if (ast_stream_and_wait(conference_bridge_user->chan, "conf-otherinparty", "")) {
00222          return -1;
00223       }
00224    }
00225    return 0;
00226 }
00227 
00228 /*!
00229  * \brief Play back an audio file to a channel
00230  *
00231  * \param conference_bridge Conference bridge they are in
00232  * \param chan Channel to play audio prompt to
00233  * \param file Prompt to play
00234  *
00235  * \return Returns 0 on success, -1 if the user hung up
00236  *
00237  * \note This function assumes that conference_bridge is locked
00238  */
00239 static int play_prompt_to_channel(struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file)
00240 {
00241    int res;
00242    ao2_unlock(conference_bridge);
00243    res = ast_stream_and_wait(chan, file, "");
00244    ao2_lock(conference_bridge);
00245    return res;
00246 }
00247 
00248 /*!
00249  * \brief Perform post-joining marked specific actions
00250  *
00251  * \param conference_bridge Conference bridge being joined
00252  * \param conference_bridge_user Conference bridge user joining
00253  *
00254  * \return Returns 0 on success, -1 if the user hung up
00255  */
00256 static int post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
00257 {
00258    if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
00259       struct conference_bridge_user *other_conference_bridge_user = NULL;
00260 
00261       /* If we are not the first marked user to join just bail out now */
00262       if (conference_bridge->markedusers >= 2) {
00263          return 0;
00264       }
00265 
00266       /* Iterate through every participant stopping MOH on them if need be */
00267       AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
00268          if (other_conference_bridge_user == conference_bridge_user) {
00269             continue;
00270          }
00271          if (ast_test_flag(&other_conference_bridge_user->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan)) {
00272             ast_moh_stop(other_conference_bridge_user->chan);
00273             ast_bridge_unsuspend(conference_bridge->bridge, other_conference_bridge_user->chan);
00274          }
00275       }
00276 
00277       /* Next play the audio file stating they are going to be placed into the conference */
00278       if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
00279          ao2_unlock(conference_bridge);
00280          ast_autoservice_start(conference_bridge_user->chan);
00281          play_sound_file(conference_bridge, "conf-placeintoconf");
00282          ast_autoservice_stop(conference_bridge_user->chan);
00283          ao2_lock(conference_bridge);
00284       }
00285 
00286       /* Finally iterate through and unmute them all */
00287       AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
00288          if (other_conference_bridge_user == conference_bridge_user) {
00289             continue;
00290          }
00291          other_conference_bridge_user->features.mute = 0;
00292       }
00293 
00294    } else {
00295       /* If a marked user already exists in the conference bridge we can just bail out now */
00296       if (conference_bridge->markedusers) {
00297          return 0;
00298       }
00299       /* Be sure we are muted so we can't talk to anybody else waiting */
00300       conference_bridge_user->features.mute = 1;
00301       /* If we have not been quieted play back that they are waiting for the leader */
00302       if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
00303          if (play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-waitforleader")) {
00304             /* user hung up while the sound was playing */
00305             return -1;
00306          }
00307       }
00308       /* Start music on hold if needed */
00309       /* We need to recheck the markedusers value here. play_prompt_to_channel unlocks the conference bridge, potentially
00310        * allowing a marked user to enter while the prompt was playing
00311        */
00312       if (!conference_bridge->markedusers && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
00313          ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00314       }
00315    }
00316    return 0;
00317 }
00318 
00319 /*!
00320  * \brief Perform post-joining non-marked specific actions
00321  *
00322  * \param conference_bridge Conference bridge being joined
00323  * \param conference_bridge_user Conference bridge user joining
00324  *
00325  * \return Returns 0 on success, -1 if the user hung up
00326  */
00327 static int post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
00328 {
00329    /* Play back audio prompt and start MOH if need be if we are the first participant */
00330    if (conference_bridge->users == 1) {
00331       /* If audio prompts have not been quieted or this prompt quieted play it on out */
00332       if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET | OPTION_NOONLYPERSON)) {
00333          if (play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-onlyperson")) {
00334             /* user hung up while the sound was playing */
00335             return -1;
00336          }
00337       }
00338       /* If we need to start music on hold on the channel do so now */
00339       /* We need to re-check the number of users in the conference bridge here because another conference bridge
00340        * participant could have joined while the above prompt was playing for the first user.
00341        */
00342       if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
00343          ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00344       }
00345       return 0;
00346    }
00347 
00348    /* Announce number of users if need be */
00349    if (ast_test_flag(&conference_bridge_user->flags, OPTION_ANNOUNCEUSERCOUNT)) {
00350       ao2_unlock(conference_bridge);
00351       if (announce_user_count(conference_bridge, conference_bridge_user)) {
00352          ao2_lock(conference_bridge);
00353          return -1;
00354       }
00355       ao2_lock(conference_bridge);
00356    }
00357 
00358    /* If we are the second participant we may need to stop music on hold on the first */
00359    if (conference_bridge->users == 2) {
00360       struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
00361 
00362       /* Temporarily suspend the above participant from the bridge so we have control to stop MOH if needed */
00363       if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
00364          ast_moh_stop(first_participant->chan);
00365          ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
00366       }
00367    }
00368    return 0;
00369 }
00370 
00371 /*!
00372  * \brief Destroy a conference bridge
00373  *
00374  * \param obj The conference bridge object
00375  *
00376  * \return Returns nothing
00377  */
00378 static void destroy_conference_bridge(void *obj)
00379 {
00380    struct conference_bridge *conference_bridge = obj;
00381 
00382    ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name);
00383 
00384    ast_mutex_destroy(&conference_bridge->playback_lock);
00385 
00386    if (conference_bridge->playback_chan) {
00387       struct ast_channel *underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
00388       ast_hangup(underlying_channel);
00389       ast_hangup(conference_bridge->playback_chan);
00390       conference_bridge->playback_chan = NULL;
00391    }
00392 
00393    /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
00394    if (conference_bridge->bridge) {
00395       ast_bridge_destroy(conference_bridge->bridge);
00396       conference_bridge->bridge = NULL;
00397    }
00398 }
00399 
00400 static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user);
00401 
00402 /*!
00403  * \brief Join a conference bridge
00404  *
00405  * \param name The conference name
00406  * \param conference_bridge_user Conference bridge user structure
00407  *
00408  * \return A pointer to the conference bridge struct, or NULL if the conference room wasn't found.
00409  */
00410 static struct conference_bridge *join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user)
00411 {
00412    struct conference_bridge *conference_bridge = NULL;
00413    struct conference_bridge tmp;
00414 
00415    ast_copy_string(tmp.name, name, sizeof(tmp.name));
00416 
00417    /* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */
00418    ao2_lock(conference_bridges);
00419 
00420    ast_debug(1, "Trying to find conference bridge '%s'\n", name);
00421 
00422    /* Attempt to find an existing conference bridge */
00423    conference_bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
00424 
00425    /* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */
00426    if (conference_bridge && conference_bridge->locked && !ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN)) {
00427       ao2_unlock(conference_bridges);
00428       ao2_ref(conference_bridge, -1);
00429       ast_debug(1, "Conference bridge '%s' is locked and caller is not an admin\n", name);
00430       ast_stream_and_wait(conference_bridge_user->chan, "conf-locked", "");
00431       return NULL;
00432    }
00433 
00434    /* If no conference bridge was found see if we can create one */
00435    if (!conference_bridge) {
00436       /* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */
00437       if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) {
00438          ao2_unlock(conference_bridges);
00439          ast_log(LOG_ERROR, "Conference bridge '%s' does not exist.\n", name);
00440          return NULL;
00441       }
00442 
00443       /* Setup conference bridge parameters */
00444       ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
00445 
00446       /* Create an actual bridge that will do the audio mixing */
00447       if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_SMART))) {
00448          ao2_ref(conference_bridge, -1);
00449          conference_bridge = NULL;
00450          ao2_unlock(conference_bridges);
00451          ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name);
00452          return NULL;
00453       }
00454 
00455       /* Setup lock for playback channel */
00456       ast_mutex_init(&conference_bridge->playback_lock);
00457 
00458       /* Link it into the conference bridges container */
00459       ao2_link(conference_bridges, conference_bridge);
00460 
00461       ast_debug(1, "Created conference bridge '%s' and linked to container '%p'\n", name, conference_bridges);
00462    }
00463 
00464    ao2_unlock(conference_bridges);
00465 
00466    /* Setup conference bridge user parameters */
00467    conference_bridge_user->conference_bridge = conference_bridge;
00468 
00469    ao2_lock(conference_bridge);
00470 
00471    /* All good to go, add them in */
00472    AST_LIST_INSERT_TAIL(&conference_bridge->users_list, conference_bridge_user, list);
00473 
00474    /* Increment the users count on the bridge, but record it as it is going to need to be known right after this */
00475    conference_bridge->users++;
00476 
00477    /* If the caller is a marked user bump up the count */
00478    if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
00479       conference_bridge->markedusers++;
00480    }
00481 
00482    /* 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 */
00483    if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER | OPTION_WAITMARKED)) {
00484       if (post_join_marked(conference_bridge, conference_bridge_user)) {
00485          ao2_unlock(conference_bridge);
00486          leave_conference_bridge(conference_bridge, conference_bridge_user);
00487          return NULL;
00488       }
00489    } else {
00490       if (post_join_unmarked(conference_bridge, conference_bridge_user)) {
00491          ao2_unlock(conference_bridge);
00492          leave_conference_bridge(conference_bridge, conference_bridge_user);
00493          return NULL;
00494       }
00495    }
00496 
00497    ao2_unlock(conference_bridge);
00498 
00499    return conference_bridge;
00500 }
00501 
00502 /*!
00503  * \brief Leave a conference bridge
00504  *
00505  * \param conference_bridge The conference bridge to leave
00506  * \param conference_bridge_user The conference bridge user structure
00507  *
00508  */
00509 static void  leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
00510 {
00511    ao2_lock(conference_bridge);
00512 
00513    /* If this caller is a marked user bump down the count */
00514    if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
00515       conference_bridge->markedusers--;
00516    }
00517 
00518    /* Decrement the users count while keeping the previous participant count */
00519    conference_bridge->users--;
00520 
00521    /* Drop conference bridge user from the list, they be going bye bye */
00522    AST_LIST_REMOVE(&conference_bridge->users_list, conference_bridge_user, list);
00523 
00524    /* If there are still users in the conference bridge we may need to do things (such as start MOH on them) */
00525    if (conference_bridge->users) {
00526       if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER) && !conference_bridge->markedusers) {
00527          struct conference_bridge_user *other_participant = NULL;
00528 
00529          /* Start out with muting everyone */
00530          AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
00531             other_participant->features.mute = 1;
00532          }
00533 
00534          /* Play back the audio prompt saying the leader has left the conference */
00535          if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
00536             ao2_unlock(conference_bridge);
00537             ast_autoservice_start(conference_bridge_user->chan);
00538             play_sound_file(conference_bridge, "conf-leaderhasleft");
00539             ast_autoservice_stop(conference_bridge_user->chan);
00540             ao2_lock(conference_bridge);
00541          }
00542 
00543          /* Now on to starting MOH if needed */
00544          AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
00545             if (ast_test_flag(&other_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) {
00546                ast_moh_start(other_participant->chan, other_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00547                ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan);
00548             }
00549          }
00550       } else if (conference_bridge->users == 1) {
00551          /* Of course if there is one other person in here we may need to start up MOH on them */
00552          struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
00553 
00554          if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
00555             ast_moh_start(first_participant->chan, first_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00556             ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
00557          }
00558       }
00559    } else {
00560       ao2_unlink(conference_bridges, conference_bridge);
00561    }
00562 
00563    /* Done mucking with the conference bridge, huzzah */
00564    ao2_unlock(conference_bridge);
00565 
00566    ao2_ref(conference_bridge, -1);
00567 }
00568 
00569 /*!
00570  * \brief Play sound file into conference bridge
00571  *
00572  * \param conference_bridge The conference bridge to play sound file into
00573  * \param filename Sound file to play
00574  *
00575  * \retval 0 success
00576  * \retval -1 failure
00577  */
00578 static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename)
00579 {
00580    struct ast_channel *underlying_channel;
00581 
00582    ast_mutex_lock(&conference_bridge->playback_lock);
00583 
00584    if (!(conference_bridge->playback_chan)) {
00585       int cause;
00586 
00587       if (!(conference_bridge->playback_chan = ast_request("Bridge", AST_FORMAT_SLINEAR, "", &cause))) {
00588          ast_mutex_unlock(&conference_bridge->playback_lock);
00589          return -1;
00590       }
00591 
00592       conference_bridge->playback_chan->bridge = conference_bridge->bridge;
00593 
00594       if (ast_call(conference_bridge->playback_chan, "", 0)) {
00595          ast_hangup(conference_bridge->playback_chan);
00596          conference_bridge->playback_chan = NULL;
00597          ast_mutex_unlock(&conference_bridge->playback_lock);
00598          return -1;
00599       }
00600 
00601       ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name);
00602 
00603       underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
00604    } else {
00605       /* Channel was already available so we just need to add it back into the bridge */
00606       underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
00607       ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL);
00608    }
00609 
00610    /* The channel is all under our control, in goes the prompt */
00611    ast_stream_and_wait(conference_bridge->playback_chan, filename, "");
00612 
00613    ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", underlying_channel->name, conference_bridge->bridge);
00614    ast_bridge_depart(conference_bridge->bridge, underlying_channel);
00615 
00616    ast_mutex_unlock(&conference_bridge->playback_lock);
00617 
00618    return 0;
00619 }
00620 
00621 /*!
00622  * \brief DTMF Menu Callback
00623  *
00624  * \param bridge Bridge this is involving
00625  * \param bridge_channel Bridged channel this is involving
00626  * \param hook_pvt User's conference bridge structure
00627  *
00628  * \retval 0 success
00629  * \retval -1 failure
00630  */
00631 static int menu_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
00632 {
00633    struct conference_bridge_user *conference_bridge_user = hook_pvt;
00634    struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge;
00635    int digit, res = 0, isadmin = ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN);
00636 
00637    /* See if music on hold is playing */
00638    ao2_lock(conference_bridge);
00639    if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
00640       /* Just us so MOH is probably indeed going, let's stop it */
00641       ast_moh_stop(bridge_channel->chan);
00642    }
00643    ao2_unlock(conference_bridge);
00644 
00645    /* Try to play back the user menu, if it fails pass this back up so the bridging core will act on it */
00646    if (ast_streamfile(bridge_channel->chan, (isadmin ? "conf-adminmenu" : "conf-usermenu"), bridge_channel->chan->language)) {
00647       res = -1;
00648       goto finished;
00649    }
00650 
00651    /* Wait for them to enter a digit from the user menu options */
00652    digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY);
00653    ast_stopstream(bridge_channel->chan);
00654 
00655    if (digit == '1') {
00656       /* 1 - Mute or unmute yourself, note we only allow manipulation if they aren't waiting for a marked user or if marked users exist */
00657       if (!ast_test_flag(&conference_bridge_user->flags, OPTION_WAITMARKED) || conference_bridge->markedusers) {
00658          conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0);
00659       }
00660       res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge_user->features.mute ? "conf-muted" : "conf-unmuted"), "");
00661    } else if (isadmin && digit == '2') {
00662       /* 2 - Unlock or lock conference */
00663       conference_bridge->locked = (!conference_bridge->locked ? 1 : 0);
00664       res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge->locked ? "conf-lockednow" : "conf-unlockednow"), "");
00665    } else if (isadmin && digit == '3') {
00666       /* 3 - Eject last user */
00667       struct conference_bridge_user *last_participant = NULL;
00668 
00669       ao2_lock(conference_bridge);
00670       if (((last_participant = AST_LIST_LAST(&conference_bridge->users_list)) == conference_bridge_user) || (ast_test_flag(&last_participant->flags, OPTION_ADMIN))) {
00671          ao2_unlock(conference_bridge);
00672          res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
00673       } else {
00674          last_participant->kicked = 1;
00675          ast_bridge_remove(conference_bridge->bridge, last_participant->chan);
00676          ao2_unlock(conference_bridge);
00677       }
00678    } else if (digit == '4') {
00679       /* 4 - Decrease listening volume */
00680       ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, -1);
00681    } else if (digit == '6') {
00682       /* 6 - Increase listening volume */
00683       ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 1);
00684    } else if (digit == '7') {
00685       /* 7 - Decrease talking volume */
00686       ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, -1);
00687    } else if (digit == '8') {
00688       /* 8 - Exit the IVR */
00689    } else if (digit == '9') {
00690       /* 9 - Increase talking volume */
00691       ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 1);
00692    } else {
00693       /* No valid option was selected */
00694       res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
00695    }
00696 
00697  finished:
00698    /* See if music on hold needs to be started back up again */
00699    ao2_lock(conference_bridge);
00700    if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
00701       ast_moh_start(bridge_channel->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00702    }
00703    ao2_unlock(conference_bridge);
00704 
00705    bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
00706 
00707    return res;
00708 }
00709 
00710 /*! \brief The ConfBridge application */
00711 static int confbridge_exec(struct ast_channel *chan, void *data)
00712 {
00713    int res = 0, volume_adjustments[2];
00714    char *parse;
00715    struct conference_bridge *conference_bridge = NULL;
00716    struct conference_bridge_user conference_bridge_user = {
00717       .chan = chan,
00718    };
00719    const char *tmp, *join_sound = NULL, *leave_sound = NULL;
00720    AST_DECLARE_APP_ARGS(args,
00721       AST_APP_ARG(conf_name);
00722       AST_APP_ARG(options);
00723    );
00724 
00725    if (ast_strlen_zero(data)) {
00726       ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
00727       return -1;
00728    }
00729 
00730    /* We need to make a copy of the input string if we are going to modify it! */
00731    parse = ast_strdupa(data);
00732 
00733    AST_STANDARD_APP_ARGS(args, parse);
00734 
00735    if (args.argc == 2) {
00736       ast_app_parse_options(app_opts, &conference_bridge_user.flags, conference_bridge_user.opt_args, args.options);
00737    }
00738 
00739    /* Look for a conference bridge matching the provided name */
00740    if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) {
00741       return -1;
00742    }
00743 
00744    /* Keep a copy of volume adjustments so we can restore them later if need be */
00745    volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
00746    volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
00747 
00748    /* Always initialize the features structure, we are in most cases always going to need it. */
00749    ast_bridge_features_init(&conference_bridge_user.features);
00750 
00751    /* If the menu option is enabled provide a user or admin menu as a custom feature hook */
00752    if (ast_test_flag(&conference_bridge_user.flags, OPTION_MENU)) {
00753       ast_bridge_features_hook(&conference_bridge_user.features, "#", menu_callback, &conference_bridge_user);
00754    }
00755 
00756    /* If the caller should be joined already muted, make it so */
00757    if (ast_test_flag(&conference_bridge_user.flags, OPTION_STARTMUTED)) {
00758       conference_bridge_user.features.mute = 1;
00759    }
00760 
00761    /* Grab join/leave sounds from the channel */
00762    ast_channel_lock(chan);
00763    if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_JOIN_SOUND"))) {
00764       join_sound = ast_strdupa(tmp);
00765    }
00766    if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_LEAVE_SOUND"))) {
00767       leave_sound = ast_strdupa(tmp);
00768    }
00769    ast_channel_unlock(chan);
00770 
00771    /* If there is 1 or more people already in the conference then play our join sound unless overridden */
00772    if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(join_sound) && conference_bridge->users >= 2) {
00773       ast_autoservice_start(chan);
00774       play_sound_file(conference_bridge, join_sound);
00775       ast_autoservice_stop(chan);
00776    }
00777 
00778    /* Join our conference bridge for real */
00779    ast_bridge_join(conference_bridge->bridge, chan, NULL, &conference_bridge_user.features);
00780 
00781    /* If there is 1 or more people (not including us) already in the conference then play our leave sound unless overridden */
00782    if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(leave_sound) && conference_bridge->users >= 2) {
00783       ast_autoservice_start(chan);
00784       play_sound_file(conference_bridge, leave_sound);
00785       ast_autoservice_stop(chan);
00786    }
00787 
00788    /* Easy as pie, depart this channel from the conference bridge */
00789    leave_conference_bridge(conference_bridge, &conference_bridge_user);
00790    conference_bridge = NULL;
00791 
00792    /* Can't forget to clean up the features structure, or else we risk a memory leak */
00793    ast_bridge_features_cleanup(&conference_bridge_user.features);
00794 
00795    /* If the user was kicked from the conference play back the audio prompt for it */
00796    if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && conference_bridge_user.kicked) {
00797       res = ast_stream_and_wait(chan, "conf-kicked", "");
00798    }
00799 
00800    /* Restore volume adjustments to previous values in case they were changed */
00801    if (volume_adjustments[0]) {
00802       ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
00803    }
00804    if (volume_adjustments[1]) {
00805       ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
00806    }
00807 
00808    return res;
00809 }
00810 
00811 /*! \brief Called when module is being unloaded */
00812 static int unload_module(void)
00813 {
00814    int res = ast_unregister_application(app);
00815 
00816    /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
00817    ao2_ref(conference_bridges, -1);
00818 
00819    return res;
00820 }
00821 
00822 /*! \brief Called when module is being loaded */
00823 static int load_module(void)
00824 {
00825    /* Create a container to hold the conference bridges */
00826    if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
00827       return AST_MODULE_LOAD_DECLINE;
00828    }
00829 
00830    if (ast_register_application_xml(app, confbridge_exec)) {
00831       ao2_ref(conference_bridges, -1);
00832       return AST_MODULE_LOAD_DECLINE;
00833    }
00834 
00835    return AST_MODULE_LOAD_SUCCESS;
00836 }
00837 
00838 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Conference Bridge Application");