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  * David Vossel <dvossel@digium.com>
00008  *
00009  * See http://www.asterisk.org for more information about
00010  * the Asterisk project. Please do not directly contact
00011  * any of the maintainers of this project for assistance;
00012  * the project provides a web site, mailing lists and IRC
00013  * channels for your use.
00014  *
00015  * This program is free software, distributed under the terms of
00016  * the GNU General Public License Version 2. See the LICENSE file
00017  * at the top of the source tree.
00018  */
00019 
00020 /*! \file
00021  *
00022  * \brief Conference Bridge application
00023  *
00024  * \author\verbatim Joshua Colp <jcolp@digium.com> \endverbatim
00025  * \author\verbatim David Vossel <dvossel@digium.com> \endverbatim
00026  *
00027  * This is a conference bridge application utilizing the bridging core.
00028  * \ingroup applications
00029  */
00030 
00031 /*! \li \ref app_confbridge.c uses the configuration file \ref confbridge.conf
00032  * \addtogroup configuration_file Configuration Files
00033  */
00034 
00035 /*!
00036  * \page confbridge.conf confbridge.conf
00037  * \verbinclude confbridge.conf.sample
00038  */
00039 
00040 /*** MODULEINFO
00041    <support_level>core</support_level>
00042  ***/
00043 
00044 #include "asterisk.h"
00045 
00046 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 433447 $")
00047 
00048 #include <stdio.h>
00049 #include <stdlib.h>
00050 #include <unistd.h>
00051 #include <string.h>
00052 #include <signal.h>
00053 
00054 #include "asterisk/cli.h"
00055 #include "asterisk/file.h"
00056 #include "asterisk/channel.h"
00057 #include "asterisk/pbx.h"
00058 #include "asterisk/pbx.h"
00059 #include "asterisk/module.h"
00060 #include "asterisk/lock.h"
00061 #include "asterisk/bridge.h"
00062 #include "asterisk/musiconhold.h"
00063 #include "asterisk/say.h"
00064 #include "asterisk/audiohook.h"
00065 #include "asterisk/astobj2.h"
00066 #include "confbridge/include/confbridge.h"
00067 #include "asterisk/paths.h"
00068 #include "asterisk/manager.h"
00069 #include "asterisk/test.h"
00070 #include "asterisk/stasis.h"
00071 #include "asterisk/stasis_bridges.h"
00072 #include "asterisk/json.h"
00073 #include "asterisk/format_cache.h"
00074 
00075 /*** DOCUMENTATION
00076    <application name="ConfBridge" language="en_US">
00077       <synopsis>
00078          Conference bridge application.
00079       </synopsis>
00080       <syntax>
00081          <parameter name="conference" required="true">
00082             <para>Name of the conference bridge.  You are not limited to just
00083             numbers.</para>
00084          </parameter>
00085          <parameter name="bridge_profile">
00086             <para>The bridge profile name from confbridge.conf.  When left blank,
00087             a dynamically built bridge profile created by the CONFBRIDGE dialplan
00088             function is searched for on the channel and used.  If no dynamic
00089             profile is present, the 'default_bridge' profile found in
00090             confbridge.conf is used. </para>
00091             <para>It is important to note that while user profiles may be unique
00092             for each participant, mixing bridge profiles on a single conference
00093             is _NOT_ recommended and will produce undefined results.</para>
00094          </parameter>
00095          <parameter name="user_profile">
00096             <para>The user profile name from confbridge.conf.  When left blank,
00097             a dynamically built user profile created by the CONFBRIDGE dialplan
00098             function is searched for on the channel and used.  If no dynamic
00099             profile is present, the 'default_user' profile found in
00100             confbridge.conf is used.</para>
00101          </parameter>
00102          <parameter name="menu">
00103             <para>The name of the DTMF menu in confbridge.conf to be applied to
00104             this channel.  When left blank, a dynamically built menu profile
00105             created by the CONFBRIDGE dialplan function is searched for on
00106             the channel and used. If no dynamic profile is present, the
00107             'default_menu' profile found in confbridge.conf is used.</para>
00108          </parameter>
00109       </syntax>
00110       <description>
00111          <para>Enters the user into a specified conference bridge.  The user can
00112          exit the conference by hangup or DTMF menu option.</para>
00113          <para>This application sets the following channel variable upon completion:</para>
00114          <variablelist>
00115             <variable name="CONFBRIDGE_RESULT">
00116                <value name="FAILED">The channel encountered an error and could not enter the conference.</value>
00117                <value name="HANGUP">The channel exited the conference by hanging up.</value>
00118                <value name="KICKED">The channel was kicked from the conference.</value>
00119                <value name="ENDMARKED">The channel left the conference as a result of the last marked user leaving.</value>
00120                <value name="DTMF">The channel pressed a DTMF sequence to exit the conference.</value>
00121             </variable>
00122          </variablelist>
00123       </description>
00124       <see-also>
00125          <ref type="application">ConfBridge</ref>
00126          <ref type="function">CONFBRIDGE</ref>
00127          <ref type="function">CONFBRIDGE_INFO</ref>
00128       </see-also>
00129    </application>
00130    <function name="CONFBRIDGE" language="en_US">
00131       <synopsis>
00132          Set a custom dynamic bridge, user, or menu profile on a channel for the ConfBridge application using the same options defined in confbridge.conf.
00133       </synopsis>
00134       <syntax>
00135          <parameter name="type" required="true">
00136             <para>Type refers to which type of profile the option belongs too.  Type can be <literal>bridge</literal>, <literal>user</literal>, or
00137             <literal>menu</literal>.</para>
00138          </parameter>
00139          <parameter name="option" required="true">
00140             <para>Option refers to <filename>confbridge.conf</filename> option that is being set dynamically on this channel, or
00141             <literal>clear</literal> to remove already applied options from the channel.</para>
00142          </parameter>
00143       </syntax>
00144       <description>
00145          <para>---- Example 1 ----</para>
00146          <para>In this example the custom set user profile on this channel will automatically be used by the ConfBridge app.</para>
00147          <para>exten => 1,1,Answer() </para>
00148          <para>exten => 1,n,Set(CONFBRIDGE(user,announce_join_leave)=yes)</para>
00149          <para>exten => 1,n,Set(CONFBRIDGE(user,startmuted)=yes)</para>
00150          <para>exten => 1,n,ConfBridge(1) </para>
00151          <para>---- Example 2 ----</para>
00152          <para>This example shows how to use a predefined user or bridge profile in confbridge.conf as a template for a dynamic profile. Here we make a admin/marked user out of the default_user profile that is already defined in confbridge.conf.</para>
00153          <para>exten => 1,1,Answer() </para>
00154          <para>exten => 1,n,Set(CONFBRIDGE(user,template)=default_user)</para>
00155          <para>exten => 1,n,Set(CONFBRIDGE(user,admin)=yes)</para>
00156          <para>exten => 1,n,Set(CONFBRIDGE(user,marked)=yes)</para>
00157          <para>exten => 1,n,ConfBridge(1)</para>
00158       </description>
00159    </function>
00160    <function name="CONFBRIDGE_INFO" language="en_US">
00161       <synopsis>
00162          Get information about a ConfBridge conference.
00163       </synopsis>
00164       <syntax>
00165          <parameter name="type" required="true">
00166             <para>Type can be <literal>parties</literal>, <literal>admins</literal>, <literal>marked</literal>, or <literal>locked</literal>.</para>
00167          </parameter>
00168          <parameter name="conf" required="true">
00169             <para>Conf refers to the name of the conference being referenced.</para>
00170          </parameter>
00171       </syntax>
00172       <description>
00173          <para>This function returns a non-negative integer for valid conference identifiers (0 or 1 for <literal>locked</literal>) and "" for invalid conference identifiers.</para>
00174       </description>
00175    </function>
00176    <manager name="ConfbridgeList" language="en_US">
00177       <synopsis>
00178          List participants in a conference.
00179       </synopsis>
00180       <syntax>
00181          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00182          <parameter name="Conference" required="true">
00183             <para>Conference number.</para>
00184          </parameter>
00185       </syntax>
00186       <description>
00187          <para>Lists all users in a particular ConfBridge conference.
00188          ConfbridgeList will follow as separate events, followed by a final event called
00189          ConfbridgeListComplete.</para>
00190       </description>
00191    </manager>
00192    <manager name="ConfbridgeListRooms" language="en_US">
00193       <synopsis>
00194          List active conferences.
00195       </synopsis>
00196       <syntax>
00197          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00198       </syntax>
00199       <description>
00200          <para>Lists data about all active conferences.
00201             ConfbridgeListRooms will follow as separate events, followed by a final event called
00202             ConfbridgeListRoomsComplete.</para>
00203       </description>
00204    </manager>
00205    <manager name="ConfbridgeMute" language="en_US">
00206       <synopsis>
00207          Mute a Confbridge user.
00208       </synopsis>
00209       <syntax>
00210          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00211          <parameter name="Conference" required="true" />
00212          <parameter name="Channel" required="true">
00213             <para>If this parameter is not a complete channel name, the first channel with this prefix will be used.</para>
00214             <para>If this parameter is "all", all channels will be muted.</para>
00215             <para>If this parameter is "participants", all non-admin channels will be muted.</para>
00216          </parameter>
00217       </syntax>
00218       <description>
00219       </description>
00220    </manager>
00221    <manager name="ConfbridgeUnmute" language="en_US">
00222       <synopsis>
00223          Unmute a Confbridge user.
00224       </synopsis>
00225       <syntax>
00226          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00227          <parameter name="Conference" required="true" />
00228          <parameter name="Channel" required="true">
00229             <para>If this parameter is not a complete channel name, the first channel with this prefix will be used.</para>
00230             <para>If this parameter is "all", all channels will be unmuted.</para>
00231             <para>If this parameter is "participants", all non-admin channels will be unmuted.</para>
00232          </parameter>
00233       </syntax>
00234       <description>
00235       </description>
00236    </manager>
00237    <manager name="ConfbridgeKick" language="en_US">
00238       <synopsis>
00239          Kick a Confbridge user.
00240       </synopsis>
00241       <syntax>
00242          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00243          <parameter name="Conference" required="true" />
00244          <parameter name="Channel" required="true" >
00245             <para>If this parameter is "all", all channels will be kicked from the conference.</para>
00246             <para>If this parameter is "participants", all non-admin channels will be kicked from the conference.</para>
00247          </parameter>
00248       </syntax>
00249       <description>
00250       </description>
00251    </manager>
00252    <manager name="ConfbridgeLock" language="en_US">
00253       <synopsis>
00254          Lock a Confbridge conference.
00255       </synopsis>
00256       <syntax>
00257          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00258          <parameter name="Conference" required="true" />
00259       </syntax>
00260       <description>
00261       </description>
00262    </manager>
00263    <manager name="ConfbridgeUnlock" language="en_US">
00264       <synopsis>
00265          Unlock a Confbridge conference.
00266       </synopsis>
00267       <syntax>
00268          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00269          <parameter name="Conference" required="true" />
00270       </syntax>
00271       <description>
00272       </description>
00273    </manager>
00274    <manager name="ConfbridgeStartRecord" language="en_US">
00275       <synopsis>
00276          Start recording a Confbridge conference.
00277       </synopsis>
00278       <syntax>
00279          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00280          <parameter name="Conference" required="true" />
00281          <parameter name="RecordFile" required="false" />
00282       </syntax>
00283       <description>
00284          <para>Start recording a conference. If recording is already present an error will be returned. If RecordFile is not provided, the default record file specified in the conference's bridge profile will be used, if that is not present either a file will automatically be generated in the monitor directory.</para>
00285       </description>
00286    </manager>
00287    <manager name="ConfbridgeStopRecord" language="en_US">
00288       <synopsis>
00289          Stop recording a Confbridge conference.
00290       </synopsis>
00291       <syntax>
00292          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00293          <parameter name="Conference" required="true" />
00294       </syntax>
00295       <description>
00296       </description>
00297    </manager>
00298    <manager name="ConfbridgeSetSingleVideoSrc" language="en_US">
00299       <synopsis>
00300          Set a conference user as the single video source distributed to all other participants.
00301       </synopsis>
00302       <syntax>
00303          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00304          <parameter name="Conference" required="true" />
00305          <parameter name="Channel" required="true">
00306             <para>If this parameter is not a complete channel name, the first channel with this prefix will be used.</para>
00307          </parameter>
00308       </syntax>
00309       <description>
00310       </description>
00311    </manager>
00312 
00313 ***/
00314 
00315 /*!
00316  * \par Playing back a file to a channel in a conference
00317  * You might notice in this application that while playing a sound file
00318  * to a channel the actual conference bridge lock is not held. This is done so
00319  * that other channels are not blocked from interacting with the conference bridge.
00320  * Unfortunately because of this it is possible for things to change after the sound file
00321  * is done being played. Data must therefore be checked after reacquiring the conference
00322  * bridge lock if it is important.
00323  */
00324 
00325 static const char app[] = "ConfBridge";
00326 
00327 /*! Number of buckets our conference bridges container can have */
00328 #define CONFERENCE_BRIDGE_BUCKETS 53
00329 
00330 /*! Initial recording filename space. */
00331 #define RECORD_FILENAME_INITIAL_SPACE  128
00332 
00333 /*! \brief Container to hold all conference bridges in progress */
00334 struct ao2_container *conference_bridges;
00335 
00336 static void leave_conference(struct confbridge_user *user);
00337 static int play_sound_number(struct confbridge_conference *conference, int say_number);
00338 static int execute_menu_entry(struct confbridge_conference *conference,
00339    struct confbridge_user *user,
00340    struct ast_bridge_channel *bridge_channel,
00341    struct conf_menu_entry *menu_entry,
00342    struct conf_menu *menu);
00343 
00344 /*! \brief Hashing function used for conference bridges container */
00345 static int conference_bridge_hash_cb(const void *obj, const int flags)
00346 {
00347    const struct confbridge_conference *conference = obj;
00348    const char *name = obj;
00349    int hash;
00350 
00351    switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
00352    default:
00353    case OBJ_POINTER:
00354       name = conference->name;
00355       /* Fall through */
00356    case OBJ_KEY:
00357       hash = ast_str_case_hash(name);
00358       break;
00359    case OBJ_PARTIAL_KEY:
00360       /* Should never happen in hash callback. */
00361       ast_assert(0);
00362       hash = 0;
00363       break;
00364    }
00365    return hash;
00366 }
00367 
00368 /*! \brief Comparison function used for conference bridges container */
00369 static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
00370 {
00371    const struct confbridge_conference *left = obj;
00372    const struct confbridge_conference *right = arg;
00373    const char *right_name = arg;
00374    int cmp;
00375 
00376    switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
00377    default:
00378    case OBJ_POINTER:
00379       right_name = right->name;
00380       /* Fall through */
00381    case OBJ_KEY:
00382       cmp = strcasecmp(left->name, right_name);
00383       break;
00384    case OBJ_PARTIAL_KEY:
00385       cmp = strncasecmp(left->name, right_name, strlen(right_name));
00386       break;
00387    }
00388    return cmp ? 0 : CMP_MATCH;
00389 }
00390 
00391 const char *conf_get_sound(enum conf_sounds sound, struct bridge_profile_sounds *custom_sounds)
00392 {
00393    switch (sound) {
00394    case CONF_SOUND_HAS_JOINED:
00395       return S_OR(custom_sounds->hasjoin, "conf-hasjoin");
00396    case CONF_SOUND_HAS_LEFT:
00397       return S_OR(custom_sounds->hasleft, "conf-hasleft");
00398    case CONF_SOUND_KICKED:
00399       return S_OR(custom_sounds->kicked, "conf-kicked");
00400    case CONF_SOUND_MUTED:
00401       return S_OR(custom_sounds->muted, "conf-muted");
00402    case CONF_SOUND_UNMUTED:
00403       return S_OR(custom_sounds->unmuted, "conf-unmuted");
00404    case CONF_SOUND_ONLY_ONE:
00405       return S_OR(custom_sounds->onlyone, "conf-onlyone");
00406    case CONF_SOUND_THERE_ARE:
00407       return S_OR(custom_sounds->thereare, "conf-thereare");
00408    case CONF_SOUND_OTHER_IN_PARTY:
00409       return S_OR(custom_sounds->otherinparty, "conf-otherinparty");
00410    case CONF_SOUND_PLACE_IN_CONF:
00411       return S_OR(custom_sounds->placeintoconf, "conf-placeintoconf");
00412    case CONF_SOUND_WAIT_FOR_LEADER:
00413       return S_OR(custom_sounds->waitforleader, "conf-waitforleader");
00414    case CONF_SOUND_LEADER_HAS_LEFT:
00415       return S_OR(custom_sounds->leaderhasleft, "conf-leaderhasleft");
00416    case CONF_SOUND_GET_PIN:
00417       return S_OR(custom_sounds->getpin, "conf-getpin");
00418    case CONF_SOUND_INVALID_PIN:
00419       return S_OR(custom_sounds->invalidpin, "conf-invalidpin");
00420    case CONF_SOUND_ONLY_PERSON:
00421       return S_OR(custom_sounds->onlyperson, "conf-onlyperson");
00422    case CONF_SOUND_LOCKED:
00423       return S_OR(custom_sounds->locked, "conf-locked");
00424    case CONF_SOUND_LOCKED_NOW:
00425       return S_OR(custom_sounds->lockednow, "conf-lockednow");
00426    case CONF_SOUND_UNLOCKED_NOW:
00427       return S_OR(custom_sounds->unlockednow, "conf-unlockednow");
00428    case CONF_SOUND_ERROR_MENU:
00429       return S_OR(custom_sounds->errormenu, "conf-errormenu");
00430    case CONF_SOUND_JOIN:
00431       return S_OR(custom_sounds->join, "confbridge-join");
00432    case CONF_SOUND_LEAVE:
00433       return S_OR(custom_sounds->leave, "confbridge-leave");
00434    case CONF_SOUND_PARTICIPANTS_MUTED:
00435       return S_OR(custom_sounds->participantsmuted, "conf-now-muted");
00436    case CONF_SOUND_PARTICIPANTS_UNMUTED:
00437       return S_OR(custom_sounds->participantsunmuted, "conf-now-unmuted");
00438    case CONF_SOUND_BEGIN:
00439       return S_OR(custom_sounds->begin, "confbridge-conf-begin");
00440    }
00441 
00442    return "";
00443 }
00444 
00445 static void send_conf_stasis(struct confbridge_conference *conference, struct ast_channel *chan,
00446    struct stasis_message_type *type, struct ast_json *extras, int channel_topic)
00447 {
00448    RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
00449    RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
00450 
00451    json_object = ast_json_pack("{s: s}",
00452       "conference", conference->name);
00453    if (!json_object) {
00454       return;
00455    }
00456 
00457    if (extras) {
00458       ast_json_object_update(json_object, extras);
00459    }
00460 
00461    ast_bridge_lock(conference->bridge);
00462    msg = ast_bridge_blob_create(type,
00463       conference->bridge,
00464       chan,
00465       json_object);
00466    ast_bridge_unlock(conference->bridge);
00467    if (!msg) {
00468       return;
00469    }
00470 
00471    if (channel_topic) {
00472       stasis_publish(ast_channel_topic(chan), msg);
00473    } else {
00474       stasis_publish(ast_bridge_topic(conference->bridge), msg);
00475    }
00476 }
00477 
00478 static void send_conf_start_event(struct confbridge_conference *conference)
00479 {
00480    send_conf_stasis(conference, NULL, confbridge_start_type(), NULL, 0);
00481 }
00482 
00483 static void send_conf_end_event(struct confbridge_conference *conference)
00484 {
00485    send_conf_stasis(conference, NULL, confbridge_end_type(), NULL, 0);
00486 }
00487 
00488 static void send_join_event(struct confbridge_user *user, struct confbridge_conference *conference)
00489 {
00490    struct ast_json *json_object;
00491 
00492    json_object = ast_json_pack("{s: b}",
00493       "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN)
00494    );
00495    if (!json_object) {
00496       return;
00497    }
00498    send_conf_stasis(conference, user->chan, confbridge_join_type(), json_object, 0);
00499    ast_json_unref(json_object);
00500 }
00501 
00502 static void send_leave_event(struct confbridge_user *user, struct confbridge_conference *conference)
00503 {
00504    struct ast_json *json_object;
00505 
00506    json_object = ast_json_pack("{s: b}",
00507       "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN)
00508    );
00509    if (!json_object) {
00510       return;
00511    }
00512    send_conf_stasis(conference, user->chan, confbridge_leave_type(), json_object, 0);
00513    ast_json_unref(json_object);
00514 }
00515 
00516 static void send_start_record_event(struct confbridge_conference *conference)
00517 {
00518    send_conf_stasis(conference, NULL, confbridge_start_record_type(), NULL, 0);
00519 }
00520 
00521 static void send_stop_record_event(struct confbridge_conference *conference)
00522 {
00523    send_conf_stasis(conference, NULL, confbridge_stop_record_type(), NULL, 0);
00524 }
00525 
00526 static void send_mute_event(struct confbridge_user *user, struct confbridge_conference *conference)
00527 {
00528    struct ast_json *json_object;
00529 
00530    json_object = ast_json_pack("{s: b}",
00531       "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN)
00532    );
00533    if (!json_object) {
00534       return;
00535    }
00536    send_conf_stasis(conference, user->chan, confbridge_mute_type(), json_object, 1);
00537    ast_json_unref(json_object);
00538 }
00539 
00540 static void send_unmute_event(struct confbridge_user *user, struct confbridge_conference *conference)
00541 {
00542    struct ast_json *json_object;
00543 
00544    json_object = ast_json_pack("{s: b}",
00545       "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN)
00546    );
00547    if (!json_object) {
00548       return;
00549    }
00550    send_conf_stasis(conference, user->chan, confbridge_unmute_type(), json_object, 1);
00551    ast_json_unref(json_object);
00552 }
00553 
00554 static void set_rec_filename(struct confbridge_conference *conference, struct ast_str **filename, int is_new)
00555 {
00556    char *rec_file = conference->b_profile.rec_file;
00557    char *ext;
00558    time_t now;
00559 
00560    if (ast_str_strlen(*filename)
00561       && ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_FILE_APPEND)
00562       && !is_new) {
00563       return;
00564    }
00565 
00566    time(&now);
00567 
00568    ast_str_reset(*filename);
00569    if (ast_strlen_zero(rec_file)) {
00570       ast_str_set(filename, 0, "confbridge-%s-%u.wav", conference->name,
00571          (unsigned int) now);
00572    } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_FILE_TIMESTAMP)) {
00573       /* insert time before file extension */
00574       ext = strrchr(rec_file, '.');
00575       if (ext) {
00576          ast_str_set_substr(filename, 0, rec_file, ext - rec_file);
00577          ast_str_append(filename, 0, "-%u%s", (unsigned int) now, ext);
00578       } else {
00579          ast_str_set(filename, 0, "%s-%u", rec_file, (unsigned int) now);
00580       }
00581    } else {
00582       ast_str_set(filename, 0, "%s", rec_file);
00583    }
00584    ast_str_append(filename, 0, ",%s%s,%s",
00585       ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_FILE_APPEND) ? "a" : "",
00586       conference->b_profile.rec_options,
00587       conference->b_profile.rec_command);
00588 }
00589 
00590 static int is_new_rec_file(const char *rec_file, struct ast_str **orig_rec_file)
00591 {
00592    if (!ast_strlen_zero(rec_file)) {
00593       if (!*orig_rec_file) {
00594          *orig_rec_file = ast_str_create(RECORD_FILENAME_INITIAL_SPACE);
00595       }
00596 
00597       if (*orig_rec_file
00598          && strcmp(ast_str_buffer(*orig_rec_file), rec_file)) {
00599          ast_str_set(orig_rec_file, 0, "%s", rec_file);
00600          return 1;
00601       }
00602    }
00603    return 0;
00604 }
00605 
00606 /*!
00607  * \internal
00608  * \brief Returns whether or not conference is being recorded.
00609  *
00610  * \param conference The bridge to check for recording
00611  *
00612  * \note Must be called with the conference locked
00613  *
00614  * \retval 1, conference is recording.
00615  * \retval 0, conference is NOT recording.
00616  */
00617 static int conf_is_recording(struct confbridge_conference *conference)
00618 {
00619    return conference->record_chan != NULL;
00620 }
00621 
00622 /*!
00623  * \internal
00624  * \brief Stop recording a conference bridge
00625  *
00626  * \param conference The conference bridge on which to stop the recording
00627  *
00628  * \note Must be called with the conference locked
00629  *
00630  * \retval -1 Failure
00631  * \retval 0 Success
00632  */
00633 static int conf_stop_record(struct confbridge_conference *conference)
00634 {
00635    struct ast_channel *chan;
00636    struct ast_frame f = { AST_FRAME_CONTROL, .subclass.integer = AST_CONTROL_HANGUP };
00637 
00638    if (!conf_is_recording(conference)) {
00639       return -1;
00640    }
00641 
00642    /* Remove the recording channel from the conference bridge. */
00643    chan = conference->record_chan;
00644    conference->record_chan = NULL;
00645    ast_queue_frame(chan, &f);
00646    ast_channel_unref(chan);
00647 
00648    ast_test_suite_event_notify("CONF_STOP_RECORD", "Message: stopped conference recording channel\r\nConference: %s", conference->b_profile.name);
00649    send_stop_record_event(conference);
00650 
00651    return 0;
00652 }
00653 
00654 /*!
00655  * \internal
00656  * \brief Start recording the conference
00657  *
00658  * \param conference The conference bridge to start recording
00659  *
00660  * \note Must be called with the conference locked
00661  *
00662  * \retval 0 success
00663  * \retval non-zero failure
00664  */
00665 static int conf_start_record(struct confbridge_conference *conference)
00666 {
00667    struct ast_app *mixmonapp;
00668    struct ast_channel *chan;
00669    struct ast_format_cap *cap;
00670    struct ast_bridge_features *features;
00671 
00672    if (conf_is_recording(conference)) {
00673       return -1;
00674    }
00675 
00676    mixmonapp = pbx_findapp("MixMonitor");
00677    if (!mixmonapp) {
00678       ast_log(LOG_WARNING, "Cannot record ConfBridge, MixMonitor app is not installed\n");
00679       return -1;
00680    }
00681 
00682    features = ast_bridge_features_new();
00683    if (!features) {
00684       return -1;
00685    }
00686    ast_set_flag(&features->feature_flags, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE);
00687 
00688    cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
00689    if (!cap) {
00690       ast_bridge_features_destroy(features);
00691       return -1;
00692    }
00693    ast_format_cap_append(cap, ast_format_slin, 0);
00694 
00695    /* Create the recording channel. */
00696    chan = ast_request("CBRec", cap, NULL, NULL, conference->name, NULL);
00697    ao2_ref(cap, -1);
00698    if (!chan) {
00699       ast_bridge_features_destroy(features);
00700       return -1;
00701    }
00702 
00703    /* Start recording. */
00704    set_rec_filename(conference, &conference->record_filename,
00705       is_new_rec_file(conference->b_profile.rec_file, &conference->orig_rec_file));
00706    ast_answer(chan);
00707    pbx_exec(chan, mixmonapp, ast_str_buffer(conference->record_filename));
00708 
00709    /* Put the channel into the conference bridge. */
00710    ast_channel_ref(chan);
00711    conference->record_chan = chan;
00712    if (ast_bridge_impart(conference->bridge, chan, NULL, features,
00713       AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
00714       ast_hangup(chan);
00715       ast_channel_unref(chan);
00716       conference->record_chan = NULL;
00717       return -1;
00718    }
00719 
00720    ast_test_suite_event_notify("CONF_START_RECORD", "Message: started conference recording channel\r\nConference: %s", conference->b_profile.name);
00721    send_start_record_event(conference);
00722 
00723    return 0;
00724 }
00725 
00726 /* \brief Playback the given filename and monitor for any dtmf interrupts.
00727  *
00728  * This function is used to playback sound files on a given channel and optionally
00729  * allow dtmf interrupts to occur.
00730  *
00731  * If the optional bridge_channel parameter is given then sound file playback
00732  * is played on that channel and dtmf interruptions are allowed. However, if
00733  * bridge_channel is not set then the channel parameter is expected to be set
00734  * instead and non interruptible playback is played on that channel.
00735  *
00736  * \param bridge_channel Bridge channel to play file on
00737  * \param channel Optional channel to play file on if bridge_channel not given
00738  * \param filename The file name to playback
00739  *
00740  * \retval -1 failure during playback, 0 on file was fully played, 1 on dtmf interrupt.
00741  */
00742 static int play_file(struct ast_bridge_channel *bridge_channel, struct ast_channel *channel,
00743            const char *filename)
00744 {
00745    struct ast_channel *chan;
00746    const char *stop_digits;
00747    int digit;
00748 
00749    if (bridge_channel) {
00750       chan = bridge_channel->chan;
00751       stop_digits = AST_DIGIT_ANY;
00752    } else {
00753       chan = channel;
00754       stop_digits = AST_DIGIT_NONE;
00755    }
00756 
00757    digit = ast_stream_and_wait(chan, filename, stop_digits);
00758    if (digit < 0) {
00759       ast_log(LOG_WARNING, "Failed to playback file '%s' to channel\n", filename);
00760       return -1;
00761    }
00762 
00763    if (digit > 0) {
00764       ast_stopstream(bridge_channel->chan);
00765       ast_bridge_channel_feature_digit_add(bridge_channel, digit);
00766       return 1;
00767    }
00768 
00769    return 0;
00770 }
00771 
00772 /*!
00773  * \internal
00774  * \brief Complain if the given sound file does not exist.
00775  *
00776  * \param filename Sound file to check if exists.
00777  *
00778  * \retval non-zero if the file exists.
00779  */
00780 static int sound_file_exists(const char *filename)
00781 {
00782    if (ast_fileexists(filename, NULL, NULL)) {
00783       return -1;
00784    }
00785    ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
00786    return 0;
00787 }
00788 
00789 /*!
00790  * \brief Announce number of users in the conference bridge to the caller
00791  *
00792  * \param conference Conference bridge to peek at
00793  * \param user Optional Caller
00794  * \param bridge_channel The bridged channel involved
00795  *
00796  * \note if caller is NULL, the announcment will be sent to all participants in the conference.
00797  * \return Returns 0 on success, -1 if the user hung up
00798  */
00799 static int announce_user_count(struct confbridge_conference *conference, struct confbridge_user *user,
00800                 struct ast_bridge_channel *bridge_channel)
00801 {
00802    const char *other_in_party = conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, conference->b_profile.sounds);
00803    const char *only_one = conf_get_sound(CONF_SOUND_ONLY_ONE, conference->b_profile.sounds);
00804    const char *there_are = conf_get_sound(CONF_SOUND_THERE_ARE, conference->b_profile.sounds);
00805 
00806    if (conference->activeusers <= 1) {
00807       /* Awww we are the only person in the conference bridge OR we only have waitmarked users */
00808       return 0;
00809    } else if (conference->activeusers == 2) {
00810       if (user) {
00811          /* Eep, there is one other person */
00812          if (play_file(bridge_channel, user->chan, only_one) < 0) {
00813             return -1;
00814          }
00815       } else {
00816          play_sound_file(conference, only_one);
00817       }
00818    } else {
00819       /* Alas multiple others in here */
00820       if (user) {
00821          if (ast_stream_and_wait(user->chan,
00822             there_are,
00823             "")) {
00824             return -1;
00825          }
00826          if (ast_say_number(user->chan, conference->activeusers - 1, "", ast_channel_language(user->chan), NULL)) {
00827             return -1;
00828          }
00829          if (play_file(bridge_channel, user->chan, other_in_party) < 0) {
00830             return -1;
00831          }
00832       } else if (sound_file_exists(there_are) && sound_file_exists(other_in_party)) {
00833          play_sound_file(conference, there_are);
00834          play_sound_number(conference, conference->activeusers - 1);
00835          play_sound_file(conference, other_in_party);
00836       }
00837    }
00838    return 0;
00839 }
00840 
00841 /*!
00842  * \brief Play back an audio file to a channel
00843  *
00844  * \param user User to play audio prompt to
00845  * \param filename Prompt to play
00846  *
00847  * \return Returns 0 on success, -1 if the user hung up
00848  * \note Generally this should be called when the conference is unlocked to avoid blocking
00849  * the entire conference while the sound is played. But don't unlock the conference bridge
00850  * in the middle of a state transition.
00851  */
00852 static int play_prompt_to_user(struct confbridge_user *user, const char *filename)
00853 {
00854    return ast_stream_and_wait(user->chan, filename, "");
00855 }
00856 
00857 static void handle_video_on_join(struct confbridge_conference *conference, struct ast_channel *chan, int marked)
00858 {
00859    /* Right now, only marked users are automatically set as the single src of video.*/
00860    if (!marked) {
00861       return;
00862    }
00863 
00864    if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED)) {
00865       int set = 1;
00866       struct confbridge_user *user = NULL;
00867 
00868       ao2_lock(conference);
00869       /* see if anyone is already the video src */
00870       AST_LIST_TRAVERSE(&conference->active_list, user, list) {
00871          if (user->chan == chan) {
00872             continue;
00873          }
00874          if (ast_bridge_is_video_src(conference->bridge, user->chan)) {
00875             set = 0;
00876             break;
00877          }
00878       }
00879       ao2_unlock(conference);
00880       if (set) {
00881          ast_bridge_set_single_src_video_mode(conference->bridge, chan);
00882       }
00883    } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
00884       /* we joined and are video capable, we override anyone else that may have already been the video feed */
00885       ast_bridge_set_single_src_video_mode(conference->bridge, chan);
00886    }
00887 }
00888 
00889 static void handle_video_on_exit(struct confbridge_conference *conference, struct ast_channel *chan)
00890 {
00891    struct confbridge_user *user = NULL;
00892 
00893    /* if this isn't a video source, nothing to update */
00894    if (!ast_bridge_is_video_src(conference->bridge, chan)) {
00895       return;
00896    }
00897 
00898    ast_bridge_remove_video_src(conference->bridge, chan);
00899 
00900    /* If in follow talker mode, make sure to restore this mode on the
00901     * bridge when a source is removed.  It is possible this channel was
00902     * only set temporarily as a video source by an AMI or DTMF action. */
00903    if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
00904       ast_bridge_set_talker_src_video_mode(conference->bridge);
00905    }
00906 
00907    /* if the video_mode isn't set to automatically pick the video source, do nothing on exit. */
00908    if (!ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) &&
00909       !ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
00910       return;
00911    }
00912 
00913    /* Make the next available marked user the video src.  */
00914    ao2_lock(conference);
00915    AST_LIST_TRAVERSE(&conference->active_list, user, list) {
00916       if (user->chan == chan) {
00917          continue;
00918       }
00919       if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
00920          ast_bridge_set_single_src_video_mode(conference->bridge, user->chan);
00921          break;
00922       }
00923    }
00924    ao2_unlock(conference);
00925 }
00926 
00927 /*!
00928  * \brief Destroy a conference bridge
00929  *
00930  * \param obj The conference bridge object
00931  *
00932  * \return Returns nothing
00933  */
00934 static void destroy_conference_bridge(void *obj)
00935 {
00936    struct confbridge_conference *conference = obj;
00937 
00938    ast_debug(1, "Destroying conference bridge '%s'\n", conference->name);
00939 
00940    if (conference->playback_chan) {
00941       conf_announce_channel_depart(conference->playback_chan);
00942       ast_hangup(conference->playback_chan);
00943       conference->playback_chan = NULL;
00944    }
00945 
00946    /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
00947    if (conference->bridge) {
00948       ast_bridge_destroy(conference->bridge, 0);
00949       conference->bridge = NULL;
00950    }
00951 
00952    ast_channel_cleanup(conference->record_chan);
00953    ast_free(conference->orig_rec_file);
00954    ast_free(conference->record_filename);
00955 
00956    conf_bridge_profile_destroy(&conference->b_profile);
00957    ast_mutex_destroy(&conference->playback_lock);
00958 }
00959 
00960 /*! \brief Call the proper join event handler for the user for the conference bridge's current state
00961  * \internal
00962  * \param user The conference bridge user that is joining
00963  * \retval 0 success
00964  * \retval -1 failure
00965  */
00966 static int handle_conf_user_join(struct confbridge_user *user)
00967 {
00968    conference_event_fn handler;
00969    if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
00970       handler = user->conference->state->join_marked;
00971    } else if (ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)) {
00972       handler = user->conference->state->join_waitmarked;
00973    } else {
00974       handler = user->conference->state->join_unmarked;
00975    }
00976 
00977    ast_assert(handler != NULL);
00978 
00979    if (!handler) {
00980       conf_invalid_event_fn(user);
00981       return -1;
00982    }
00983 
00984    handler(user);
00985 
00986    return 0;
00987 }
00988 
00989 /*! \brief Call the proper leave event handler for the user for the conference bridge's current state
00990  * \internal
00991  * \param user The conference bridge user that is leaving
00992  * \retval 0 success
00993  * \retval -1 failure
00994  */
00995 static int handle_conf_user_leave(struct confbridge_user *user)
00996 {
00997    conference_event_fn handler;
00998    if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
00999       handler = user->conference->state->leave_marked;
01000    } else if (ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)) {
01001       handler = user->conference->state->leave_waitmarked;
01002    } else {
01003       handler = user->conference->state->leave_unmarked;
01004    }
01005 
01006    ast_assert(handler != NULL);
01007 
01008    if (!handler) {
01009       /* This should never happen. If it does, though, it is bad. The user will not have been removed
01010        * from the appropriate list, so counts will be off and stuff. The conference won't be torn down, etc.
01011        * Shouldn't happen, though. */
01012       conf_invalid_event_fn(user);
01013       return -1;
01014    }
01015 
01016    handler(user);
01017 
01018    return 0;
01019 }
01020 
01021 void conf_update_user_mute(struct confbridge_user *user)
01022 {
01023    int mute_user;
01024    int mute_system;
01025    int mute_effective;
01026 
01027    /* User level mute request. */
01028    mute_user = user->muted;
01029 
01030    /* System level mute request. */
01031    mute_system = user->playing_moh
01032       /*
01033        * Do not allow waitmarked users to talk to anyone unless there
01034        * is a marked user present.
01035        */
01036       || (!user->conference->markedusers
01037          && ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED));
01038 
01039    mute_effective = mute_user || mute_system;
01040 
01041    ast_debug(1, "User %s is %s: user:%d system:%d.\n",
01042       ast_channel_name(user->chan), mute_effective ? "muted" : "unmuted",
01043       mute_user, mute_system);
01044    user->features.mute = mute_effective;
01045    ast_test_suite_event_notify("CONF_MUTE_UPDATE",
01046       "Mode: %s\r\n"
01047       "Conference: %s\r\n"
01048       "Channel: %s",
01049       mute_effective ? "muted" : "unmuted",
01050       user->b_profile.name,
01051       ast_channel_name(user->chan));
01052 }
01053 
01054 void conf_moh_stop(struct confbridge_user *user)
01055 {
01056    user->playing_moh = 0;
01057    if (!user->suspended_moh) {
01058       int in_bridge;
01059 
01060       /*
01061        * Locking the ast_bridge here is the only way to hold off the
01062        * call to ast_bridge_join() in confbridge_exec() from
01063        * interfering with the bridge and MOH operations here.
01064        */
01065       ast_bridge_lock(user->conference->bridge);
01066 
01067       /*
01068        * Temporarily suspend the user from the bridge so we have
01069        * control to stop MOH if needed.
01070        */
01071       in_bridge = !ast_bridge_suspend(user->conference->bridge, user->chan);
01072       ast_moh_stop(user->chan);
01073       if (in_bridge) {
01074          ast_bridge_unsuspend(user->conference->bridge, user->chan);
01075       }
01076 
01077       ast_bridge_unlock(user->conference->bridge);
01078    }
01079 }
01080 
01081 void conf_moh_start(struct confbridge_user *user)
01082 {
01083    user->playing_moh = 1;
01084    if (!user->suspended_moh) {
01085       int in_bridge;
01086 
01087       /*
01088        * Locking the ast_bridge here is the only way to hold off the
01089        * call to ast_bridge_join() in confbridge_exec() from
01090        * interfering with the bridge and MOH operations here.
01091        */
01092       ast_bridge_lock(user->conference->bridge);
01093 
01094       /*
01095        * Temporarily suspend the user from the bridge so we have
01096        * control to start MOH if needed.
01097        */
01098       in_bridge = !ast_bridge_suspend(user->conference->bridge, user->chan);
01099       ast_moh_start(user->chan, user->u_profile.moh_class, NULL);
01100       if (in_bridge) {
01101          ast_bridge_unsuspend(user->conference->bridge, user->chan);
01102       }
01103 
01104       ast_bridge_unlock(user->conference->bridge);
01105    }
01106 }
01107 
01108 /*!
01109  * \internal
01110  * \brief Unsuspend MOH for the conference user.
01111  *
01112  * \param user Conference user to unsuspend MOH on.
01113  *
01114  * \return Nothing
01115  */
01116 static void conf_moh_unsuspend(struct confbridge_user *user)
01117 {
01118    ao2_lock(user->conference);
01119    if (--user->suspended_moh == 0 && user->playing_moh) {
01120       ast_moh_start(user->chan, user->u_profile.moh_class, NULL);
01121    }
01122    ao2_unlock(user->conference);
01123 }
01124 
01125 /*!
01126  * \internal
01127  * \brief Suspend MOH for the conference user.
01128  *
01129  * \param user Conference user to suspend MOH on.
01130  *
01131  * \return Nothing
01132  */
01133 static void conf_moh_suspend(struct confbridge_user *user)
01134 {
01135    ao2_lock(user->conference);
01136    if (user->suspended_moh++ == 0 && user->playing_moh) {
01137       ast_moh_stop(user->chan);
01138    }
01139    ao2_unlock(user->conference);
01140 }
01141 
01142 int conf_handle_inactive_waitmarked(struct confbridge_user *user)
01143 {
01144    /* If we have not been quieted play back that they are waiting for the leader */
01145    if (!ast_test_flag(&user->u_profile, USER_OPT_QUIET) && play_prompt_to_user(user,
01146          conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, user->b_profile.sounds))) {
01147       /* user hungup while the sound was playing */
01148       return -1;
01149    }
01150    return 0;
01151 }
01152 
01153 int conf_handle_only_unmarked(struct confbridge_user *user)
01154 {
01155    /* If audio prompts have not been quieted or this prompt quieted play it on out */
01156    if (!ast_test_flag(&user->u_profile, USER_OPT_QUIET | USER_OPT_NOONLYPERSON)) {
01157       if (play_prompt_to_user(user,
01158          conf_get_sound(CONF_SOUND_ONLY_PERSON, user->b_profile.sounds))) {
01159          /* user hungup while the sound was playing */
01160          return -1;
01161       }
01162    }
01163    return 0;
01164 }
01165 
01166 int conf_add_post_join_action(struct confbridge_user *user, int (*func)(struct confbridge_user *user))
01167 {
01168    struct post_join_action *action;
01169    if (!(action = ast_calloc(1, sizeof(*action)))) {
01170       return -1;
01171    }
01172    action->func = func;
01173    AST_LIST_INSERT_TAIL(&user->post_join_list, action, list);
01174    return 0;
01175 }
01176 
01177 
01178 void conf_handle_first_join(struct confbridge_conference *conference)
01179 {
01180    ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "confbridge:%s", conference->name);
01181 }
01182 
01183 void conf_handle_second_active(struct confbridge_conference *conference)
01184 {
01185    /* If we are the second participant we may need to stop music on hold on the first */
01186    struct confbridge_user *first_user = AST_LIST_FIRST(&conference->active_list);
01187 
01188    if (ast_test_flag(&first_user->u_profile, USER_OPT_MUSICONHOLD)) {
01189       conf_moh_stop(first_user);
01190    }
01191    conf_update_user_mute(first_user);
01192 }
01193 
01194 void conf_ended(struct confbridge_conference *conference)
01195 {
01196    /* Called with a reference to conference */
01197    ao2_unlink(conference_bridges, conference);
01198    send_conf_end_event(conference);
01199    ao2_lock(conference);
01200    conf_stop_record(conference);
01201    ao2_unlock(conference);
01202 }
01203 
01204 /*!
01205  * \brief Join a conference bridge
01206  *
01207  * \param conference_name The conference name
01208  * \param user Conference bridge user structure
01209  *
01210  * \return A pointer to the conference bridge struct, or NULL if the conference room wasn't found.
01211  */
01212 static struct confbridge_conference *join_conference_bridge(const char *conference_name, struct confbridge_user *user)
01213 {
01214    struct confbridge_conference *conference;
01215    struct post_join_action *action;
01216    int max_members_reached = 0;
01217 
01218    /* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */
01219    ao2_lock(conference_bridges);
01220 
01221    ast_debug(1, "Trying to find conference bridge '%s'\n", conference_name);
01222 
01223    /* Attempt to find an existing conference bridge */
01224    conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
01225    if (conference && conference->b_profile.max_members) {
01226       max_members_reached = conference->b_profile.max_members > conference->activeusers ? 0 : 1;
01227    }
01228 
01229    /* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */
01230    if (conference && (max_members_reached || conference->locked) && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
01231       ao2_unlock(conference_bridges);
01232       ao2_ref(conference, -1);
01233       ast_debug(1, "Conference '%s' is locked and caller is not an admin\n", conference_name);
01234       ast_stream_and_wait(user->chan,
01235             conf_get_sound(CONF_SOUND_LOCKED, user->b_profile.sounds),
01236             "");
01237       return NULL;
01238    }
01239 
01240    /* If no conference bridge was found see if we can create one */
01241    if (!conference) {
01242       /* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */
01243       if (!(conference = ao2_alloc(sizeof(*conference), destroy_conference_bridge))) {
01244          ao2_unlock(conference_bridges);
01245          ast_log(LOG_ERROR, "Conference '%s' could not be created.\n", conference_name);
01246          return NULL;
01247       }
01248 
01249       /* Setup lock for playback channel */
01250       ast_mutex_init(&conference->playback_lock);
01251 
01252       /* Setup for the record channel */
01253       conference->record_filename = ast_str_create(RECORD_FILENAME_INITIAL_SPACE);
01254       if (!conference->record_filename) {
01255          ao2_ref(conference, -1);
01256          ao2_unlock(conference_bridges);
01257          return NULL;
01258       }
01259 
01260       /* Setup conference bridge parameters */
01261       ast_copy_string(conference->name, conference_name, sizeof(conference->name));
01262       conf_bridge_profile_copy(&conference->b_profile, &user->b_profile);
01263 
01264       /* Create an actual bridge that will do the audio mixing */
01265       conference->bridge = ast_bridge_base_new(AST_BRIDGE_CAPABILITY_MULTIMIX,
01266          AST_BRIDGE_FLAG_MASQUERADE_ONLY | AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY,
01267          app, conference_name, NULL);
01268       if (!conference->bridge) {
01269          ao2_ref(conference, -1);
01270          conference = NULL;
01271          ao2_unlock(conference_bridges);
01272          ast_log(LOG_ERROR, "Conference '%s' mixing bridge could not be created.\n", conference_name);
01273          return NULL;
01274       }
01275 
01276       /* Set the internal sample rate on the bridge from the bridge profile */
01277       ast_bridge_set_internal_sample_rate(conference->bridge, conference->b_profile.internal_sample_rate);
01278       /* Set the internal mixing interval on the bridge from the bridge profile */
01279       ast_bridge_set_mixing_interval(conference->bridge, conference->b_profile.mix_interval);
01280 
01281       if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
01282          ast_bridge_set_talker_src_video_mode(conference->bridge);
01283       }
01284 
01285       /* Link it into the conference bridges container */
01286       if (!ao2_link(conference_bridges, conference)) {
01287          ao2_ref(conference, -1);
01288          conference = NULL;
01289          ao2_unlock(conference_bridges);
01290          ast_log(LOG_ERROR,
01291             "Conference '%s' could not be added to the conferences list.\n", conference_name);
01292          return NULL;
01293       }
01294 
01295       /* Set the initial state to EMPTY */
01296       conference->state = CONF_STATE_EMPTY;
01297 
01298       if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_CONFERENCE)) {
01299          ao2_lock(conference);
01300          conf_start_record(conference);
01301          ao2_unlock(conference);
01302       }
01303 
01304       send_conf_start_event(conference);
01305       ast_debug(1, "Created conference '%s' and linked to container.\n", conference_name);
01306    }
01307 
01308    ao2_unlock(conference_bridges);
01309 
01310    /* Setup conference bridge user parameters */
01311    user->conference = conference;
01312 
01313    ao2_lock(conference);
01314 
01315    /*
01316     * Suspend any MOH until the user actually joins the bridge of
01317     * the conference.  This way any pre-join file playback does not
01318     * need to worry about MOH.
01319     */
01320    user->suspended_moh = 1;
01321 
01322    if (handle_conf_user_join(user)) {
01323       /* Invalid event, nothing was done, so we don't want to process a leave. */
01324       ao2_unlock(conference);
01325       ao2_ref(conference, -1);
01326       return NULL;
01327    }
01328 
01329    if (ast_check_hangup(user->chan)) {
01330       ao2_unlock(conference);
01331       leave_conference(user);
01332       return NULL;
01333    }
01334 
01335    ao2_unlock(conference);
01336 
01337    /* If an announcement is to be played play it */
01338    if (!ast_strlen_zero(user->u_profile.announcement)) {
01339       if (play_prompt_to_user(user,
01340          user->u_profile.announcement)) {
01341          leave_conference(user);
01342          return NULL;
01343       }
01344    }
01345 
01346    /* Announce number of users if need be */
01347    if (ast_test_flag(&user->u_profile, USER_OPT_ANNOUNCEUSERCOUNT)) {
01348       if (announce_user_count(conference, user, NULL)) {
01349          leave_conference(user);
01350          return NULL;
01351       }
01352    }
01353 
01354    if (ast_test_flag(&user->u_profile, USER_OPT_ANNOUNCEUSERCOUNTALL) &&
01355       (conference->activeusers > user->u_profile.announce_user_count_all_after)) {
01356       int user_count_res;
01357 
01358       /*
01359        * We have to autoservice the new user because he has not quite
01360        * joined the conference yet.
01361        */
01362       ast_autoservice_start(user->chan);
01363       user_count_res = announce_user_count(conference, NULL, NULL);
01364       ast_autoservice_stop(user->chan);
01365       if (user_count_res) {
01366          leave_conference(user);
01367          return NULL;
01368       }
01369    }
01370 
01371    /* Handle post-join actions */
01372    while ((action = AST_LIST_REMOVE_HEAD(&user->post_join_list, list))) {
01373       action->func(user);
01374       ast_free(action);
01375    }
01376 
01377    return conference;
01378 }
01379 
01380 /*!
01381  * \brief Leave a conference
01382  *
01383  * \param user The conference user
01384  */
01385 static void leave_conference(struct confbridge_user *user)
01386 {
01387    struct post_join_action *action;
01388 
01389    ao2_lock(user->conference);
01390    handle_conf_user_leave(user);
01391    ao2_unlock(user->conference);
01392 
01393    /* Discard any post-join actions */
01394    while ((action = AST_LIST_REMOVE_HEAD(&user->post_join_list, list))) {
01395       ast_free(action);
01396    }
01397 
01398    /* Done mucking with the conference, huzzah */
01399    ao2_ref(user->conference, -1);
01400    user->conference = NULL;
01401 }
01402 
01403 /*!
01404  * \internal
01405  * \brief Allocate playback channel for a conference.
01406  * \pre expects conference to be locked before calling this function
01407  */
01408 static int alloc_playback_chan(struct confbridge_conference *conference)
01409 {
01410    struct ast_format_cap *cap;
01411 
01412    cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
01413    if (!cap) {
01414       return -1;
01415    }
01416    ast_format_cap_append(cap, ast_format_slin, 0);
01417    conference->playback_chan = ast_request("CBAnn", cap, NULL, NULL,
01418       conference->name, NULL);
01419    ao2_ref(cap, -1);
01420    if (!conference->playback_chan) {
01421       return -1;
01422    }
01423 
01424    /* To make sure playback_chan has the same language of that profile */
01425    ast_channel_lock(conference->playback_chan);
01426    ast_channel_language_set(conference->playback_chan, conference->b_profile.language);
01427    ast_channel_unlock(conference->playback_chan);
01428 
01429    ast_debug(1, "Created announcer channel '%s' to conference bridge '%s'\n",
01430       ast_channel_name(conference->playback_chan), conference->name);
01431    return 0;
01432 }
01433 
01434 static int play_sound_helper(struct confbridge_conference *conference, const char *filename, int say_number)
01435 {
01436    /* Do not waste resources trying to play files that do not exist */
01437    if (!ast_strlen_zero(filename) && !sound_file_exists(filename)) {
01438       return 0;
01439    }
01440 
01441    ast_mutex_lock(&conference->playback_lock);
01442    if (!conference->playback_chan && alloc_playback_chan(conference)) {
01443       ast_mutex_unlock(&conference->playback_lock);
01444       return -1;
01445    }
01446    if (conf_announce_channel_push(conference->playback_chan)) {
01447       ast_mutex_unlock(&conference->playback_lock);
01448       return -1;
01449    }
01450 
01451    /* The channel is all under our control, in goes the prompt */
01452    if (!ast_strlen_zero(filename)) {
01453       ast_stream_and_wait(conference->playback_chan, filename, "");
01454    } else if (say_number >= 0) {
01455       ast_say_number(conference->playback_chan, say_number, "",
01456          ast_channel_language(conference->playback_chan), NULL);
01457    }
01458 
01459    ast_debug(1, "Departing announcer channel '%s' from conference bridge '%s'\n",
01460       ast_channel_name(conference->playback_chan), conference->name);
01461    conf_announce_channel_depart(conference->playback_chan);
01462 
01463    ast_mutex_unlock(&conference->playback_lock);
01464 
01465    return 0;
01466 }
01467 
01468 int play_sound_file(struct confbridge_conference *conference, const char *filename)
01469 {
01470    return play_sound_helper(conference, filename, -1);
01471 }
01472 
01473 /*!
01474  * \brief Play number into the conference bridge
01475  *
01476  * \param conference The conference bridge to say the number into
01477  * \param say_number number to say
01478  *
01479  * \retval 0 success
01480  * \retval -1 failure
01481  */
01482 static int play_sound_number(struct confbridge_conference *conference, int say_number)
01483 {
01484    return play_sound_helper(conference, NULL, say_number);
01485 }
01486 
01487 static int conf_handle_talker_cb(struct ast_bridge_channel *bridge_channel, void *hook_pvt, int talking)
01488 {
01489    const struct confbridge_user *user = hook_pvt;
01490    RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup);
01491    struct ast_json *talking_extras;
01492 
01493    conference = ao2_find(conference_bridges, user->conference->name, OBJ_KEY);
01494    if (!conference) {
01495       /* Remove the hook since the conference does not exist. */
01496       return -1;
01497    }
01498 
01499    talking_extras = ast_json_pack("{s: s, s: b}",
01500       "talking_status", talking ? "on" : "off",
01501       "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN));
01502    if (!talking_extras) {
01503       return 0;
01504    }
01505 
01506    send_conf_stasis(conference, bridge_channel->chan, confbridge_talking_type(), talking_extras, 0);
01507    ast_json_unref(talking_extras);
01508    return 0;
01509 }
01510 
01511 static int conf_get_pin(struct ast_channel *chan, struct confbridge_user *user)
01512 {
01513    char pin_guess[MAX_PIN+1] = { 0, };
01514    const char *pin = user->u_profile.pin;
01515    char *tmp = pin_guess;
01516    int i, res;
01517    unsigned int len = MAX_PIN ;
01518 
01519    /* give them three tries to get the pin right */
01520    for (i = 0; i < 3; i++) {
01521       if (ast_app_getdata(chan,
01522          conf_get_sound(CONF_SOUND_GET_PIN, user->b_profile.sounds),
01523          tmp, len, 0) >= 0) {
01524          if (!strcasecmp(pin, pin_guess)) {
01525             return 0;
01526          }
01527       }
01528       ast_streamfile(chan,
01529          conf_get_sound(CONF_SOUND_INVALID_PIN, user->b_profile.sounds),
01530          ast_channel_language(chan));
01531       res = ast_waitstream(chan, AST_DIGIT_ANY);
01532       if (res > 0) {
01533          /* Account for digit already read during ivalid pin playback
01534           * resetting pin buf. */
01535          pin_guess[0] = res;
01536          pin_guess[1] = '\0';
01537          tmp = pin_guess + 1;
01538          len = MAX_PIN - 1;
01539       } else {
01540          /* reset pin buf as empty buffer. */
01541          tmp = pin_guess;
01542          len = MAX_PIN;
01543       }
01544    }
01545    return -1;
01546 }
01547 
01548 static int conf_rec_name(struct confbridge_user *user, const char *conf_name)
01549 {
01550    char destdir[PATH_MAX];
01551    int res;
01552    int duration = 20;
01553 
01554    snprintf(destdir, sizeof(destdir), "%s/confbridge", ast_config_AST_SPOOL_DIR);
01555 
01556    if (ast_mkdir(destdir, 0777) != 0) {
01557       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
01558       return -1;
01559    }
01560    snprintf(user->name_rec_location, sizeof(user->name_rec_location),
01561        "%s/confbridge-name-%s-%s", destdir,
01562        conf_name, ast_channel_uniqueid(user->chan));
01563 
01564    if (!(ast_test_flag(&user->u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE_REVIEW))) {
01565       res = ast_play_and_record(user->chan,
01566          "vm-rec-name",
01567          user->name_rec_location,
01568          10,
01569          "sln",
01570          &duration,
01571          NULL,
01572          ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE),
01573          0,
01574          NULL);
01575    } else {
01576       res = ast_record_review(user->chan,
01577          "vm-rec-name",
01578          user->name_rec_location,
01579          10,
01580          "sln",
01581          &duration,
01582          NULL);
01583    }
01584 
01585    if (res == -1) {
01586       user->name_rec_location[0] = '\0';
01587       return -1;
01588    }
01589    return 0;
01590 }
01591 
01592 /*! \brief The ConfBridge application */
01593 static int confbridge_exec(struct ast_channel *chan, const char *data)
01594 {
01595    int res = 0, volume_adjustments[2];
01596    int quiet = 0;
01597    char *parse;
01598    const char *b_profile_name = NULL;
01599    const char *u_profile_name = NULL;
01600    const char *menu_profile_name = NULL;
01601    struct confbridge_conference *conference = NULL;
01602    struct confbridge_user user = {
01603       .chan = chan,
01604       .tech_args.talking_threshold = DEFAULT_TALKING_THRESHOLD,
01605       .tech_args.silence_threshold = DEFAULT_SILENCE_THRESHOLD,
01606       .tech_args.drop_silence = 0,
01607    };
01608    AST_DECLARE_APP_ARGS(args,
01609       AST_APP_ARG(conf_name);
01610       AST_APP_ARG(b_profile_name);
01611       AST_APP_ARG(u_profile_name);
01612       AST_APP_ARG(menu_profile_name);
01613    );
01614 
01615    if (ast_channel_state(chan) != AST_STATE_UP) {
01616       ast_answer(chan);
01617    }
01618 
01619    if (ast_bridge_features_init(&user.features)) {
01620       pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
01621       res = -1;
01622       goto confbridge_cleanup;
01623    }
01624 
01625    /* We need to make a copy of the input string if we are going to modify it! */
01626    parse = ast_strdupa(data);
01627 
01628    AST_STANDARD_APP_ARGS(args, parse);
01629 
01630    if (ast_strlen_zero(args.conf_name)) {
01631       pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
01632       ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
01633       res = -1;
01634       goto confbridge_cleanup;
01635    }
01636 
01637    if (strlen(args.conf_name) >= MAX_CONF_NAME) {
01638       pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
01639       ast_log(LOG_WARNING, "%s does not accept conference names longer than %d\n", app, MAX_CONF_NAME - 1);
01640       res = -1;
01641       goto confbridge_cleanup;
01642    }
01643 
01644    /* bridge profile name */
01645    if (args.argc > 1 && !ast_strlen_zero(args.b_profile_name)) {
01646       b_profile_name = args.b_profile_name;
01647    }
01648    if (!conf_find_bridge_profile(chan, b_profile_name, &user.b_profile)) {
01649       pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
01650       ast_log(LOG_WARNING, "Conference bridge profile %s does not exist\n", b_profile_name ?
01651          b_profile_name : DEFAULT_BRIDGE_PROFILE);
01652       res = -1;
01653       goto confbridge_cleanup;
01654    }
01655 
01656    /* user profile name */
01657    if (args.argc > 2 && !ast_strlen_zero(args.u_profile_name)) {
01658       u_profile_name = args.u_profile_name;
01659    }
01660    if (!conf_find_user_profile(chan, u_profile_name, &user.u_profile)) {
01661       pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
01662       ast_log(LOG_WARNING, "Conference user profile %s does not exist\n", u_profile_name ?
01663          u_profile_name : DEFAULT_USER_PROFILE);
01664       res = -1;
01665       goto confbridge_cleanup;
01666    }
01667 
01668    quiet = ast_test_flag(&user.u_profile, USER_OPT_QUIET);
01669 
01670    /* ask for a PIN immediately after finding user profile.  This has to be
01671     * prompted for requardless of quiet setting. */
01672    if (!ast_strlen_zero(user.u_profile.pin)) {
01673       if (conf_get_pin(chan, &user)) {
01674          pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
01675          res = -1; /* invalid PIN */
01676          goto confbridge_cleanup;
01677       }
01678    }
01679 
01680    /* See if we need them to record a intro name */
01681    if (!quiet &&
01682       (ast_test_flag(&user.u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE) ||
01683       (ast_test_flag(&user.u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE_REVIEW)))) {
01684       conf_rec_name(&user, args.conf_name);
01685    }
01686 
01687    /* menu name */
01688    if (args.argc > 3 && !ast_strlen_zero(args.menu_profile_name)) {
01689       menu_profile_name = args.menu_profile_name;
01690    }
01691 
01692    if (conf_set_menu_to_user(chan, &user, menu_profile_name)) {
01693       pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
01694       ast_log(LOG_WARNING, "Conference menu profile %s does not exist\n", menu_profile_name ?
01695          menu_profile_name : DEFAULT_MENU_PROFILE);
01696       res = -1;
01697       goto confbridge_cleanup;
01698    }
01699 
01700    /* Set if DTMF should pass through for this user or not */
01701    if (ast_test_flag(&user.u_profile, USER_OPT_DTMF_PASS)) {
01702       user.features.dtmf_passthrough = 1;
01703    } else {
01704       user.features.dtmf_passthrough = 0;
01705    }
01706 
01707    /* Set dsp threshold values if present */
01708    if (user.u_profile.talking_threshold) {
01709       user.tech_args.talking_threshold = user.u_profile.talking_threshold;
01710    }
01711    if (user.u_profile.silence_threshold) {
01712       user.tech_args.silence_threshold = user.u_profile.silence_threshold;
01713    }
01714 
01715    /* Set a talker indicate call back if talking detection is requested */
01716    if (ast_test_flag(&user.u_profile, USER_OPT_TALKER_DETECT)) {
01717       if (ast_bridge_talk_detector_hook(&user.features, conf_handle_talker_cb,
01718          &user, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
01719          pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
01720          res = -1;
01721          goto confbridge_cleanup;
01722       }
01723    }
01724 
01725    /* If the caller should be joined already muted, set the flag before we join. */
01726    if (ast_test_flag(&user.u_profile, USER_OPT_STARTMUTED)) {
01727       /* Set user level mute request. */
01728       user.muted = 1;
01729    }
01730 
01731    /* Look for a conference bridge matching the provided name */
01732    if (!(conference = join_conference_bridge(args.conf_name, &user))) {
01733       pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
01734       res = -1;
01735       goto confbridge_cleanup;
01736    }
01737 
01738    /* Keep a copy of volume adjustments so we can restore them later if need be */
01739    volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
01740    volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
01741 
01742    if (ast_test_flag(&user.u_profile, USER_OPT_DROP_SILENCE)) {
01743       user.tech_args.drop_silence = 1;
01744    }
01745 
01746    if (ast_test_flag(&user.u_profile, USER_OPT_JITTERBUFFER)) {
01747       char *func_jb;
01748       if ((func_jb = ast_module_helper("", "func_jitterbuffer", 0, 0, 0, 0))) {
01749          ast_free(func_jb);
01750          ast_func_write(chan, "JITTERBUFFER(adaptive)", "default");
01751       }
01752    }
01753 
01754    if (ast_test_flag(&user.u_profile, USER_OPT_DENOISE)) {
01755       char *mod_speex;
01756       /* Reduce background noise from each participant */
01757       if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
01758          ast_free(mod_speex);
01759          ast_func_write(chan, "DENOISE(rx)", "on");
01760       }
01761    }
01762 
01763    /* if this user has a intro, play it before entering */
01764    if (!ast_strlen_zero(user.name_rec_location)) {
01765       ast_autoservice_start(chan);
01766       play_sound_file(conference, user.name_rec_location);
01767       play_sound_file(conference,
01768          conf_get_sound(CONF_SOUND_HAS_JOINED, user.b_profile.sounds));
01769       ast_autoservice_stop(chan);
01770    }
01771 
01772    /* Play the Join sound to both the conference and the user entering. */
01773    if (!quiet) {
01774       const char *join_sound = conf_get_sound(CONF_SOUND_JOIN, user.b_profile.sounds);
01775 
01776       ast_stream_and_wait(chan, join_sound, "");
01777       ast_autoservice_start(chan);
01778       play_sound_file(conference, join_sound);
01779       ast_autoservice_stop(chan);
01780    }
01781 
01782    /* See if we need to automatically set this user as a video source or not */
01783    handle_video_on_join(conference, user.chan, ast_test_flag(&user.u_profile, USER_OPT_MARKEDUSER));
01784 
01785    conf_moh_unsuspend(&user);
01786 
01787    /* Join our conference bridge for real */
01788    send_join_event(&user, conference);
01789    ast_bridge_join(conference->bridge,
01790       chan,
01791       NULL,
01792       &user.features,
01793       &user.tech_args,
01794       0);
01795 
01796    if (!user.kicked && ast_check_hangup(chan)) {
01797       pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "HANGUP");
01798    }
01799 
01800    send_leave_event(&user, conference);
01801 
01802    /* if we're shutting down, don't attempt to do further processing */
01803    if (ast_shutting_down()) {
01804       /*
01805        * Not taking any new calls at this time.  We cannot create
01806        * the announcer channel if this is the first channel into
01807        * the conference and we certainly cannot create any
01808        * recording channel.
01809        */
01810       leave_conference(&user);
01811       conference = NULL;
01812       goto confbridge_cleanup;
01813    }
01814 
01815    /* If this user was a video source, we need to clean up and possibly pick a new source. */
01816    handle_video_on_exit(conference, user.chan);
01817 
01818    /* if this user has a intro, play it when leaving */
01819    if (!quiet && !ast_strlen_zero(user.name_rec_location)) {
01820       ast_autoservice_start(chan);
01821       play_sound_file(conference, user.name_rec_location);
01822       play_sound_file(conference,
01823          conf_get_sound(CONF_SOUND_HAS_LEFT, user.b_profile.sounds));
01824       ast_autoservice_stop(chan);
01825    }
01826 
01827    /* play the leave sound */
01828    if (!quiet) {
01829       const char *leave_sound = conf_get_sound(CONF_SOUND_LEAVE, user.b_profile.sounds);
01830       ast_autoservice_start(chan);
01831       play_sound_file(conference, leave_sound);
01832       ast_autoservice_stop(chan);
01833    }
01834 
01835    /* Easy as pie, depart this channel from the conference bridge */
01836    leave_conference(&user);
01837    conference = NULL;
01838 
01839    /* If the user was kicked from the conference play back the audio prompt for it */
01840    if (!quiet && user.kicked) {
01841       res = ast_stream_and_wait(chan,
01842          conf_get_sound(CONF_SOUND_KICKED, user.b_profile.sounds),
01843          "");
01844    }
01845 
01846    /* Restore volume adjustments to previous values in case they were changed */
01847    if (volume_adjustments[0]) {
01848       ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
01849    }
01850    if (volume_adjustments[1]) {
01851       ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
01852    }
01853 
01854    if (!ast_strlen_zero(user.name_rec_location)) {
01855       ast_filedelete(user.name_rec_location, NULL);
01856    }
01857 
01858 confbridge_cleanup:
01859    ast_bridge_features_cleanup(&user.features);
01860    conf_bridge_profile_destroy(&user.b_profile);
01861    return res;
01862 }
01863 
01864 static int action_toggle_mute(struct confbridge_conference *conference,
01865                struct confbridge_user *user,
01866                struct ast_bridge_channel *bridge_channel)
01867 {
01868    int mute;
01869 
01870    /* Toggle user level mute request. */
01871    mute = !user->muted;
01872    user->muted = mute;
01873 
01874    conf_update_user_mute(user);
01875    ast_test_suite_event_notify("CONF_MUTE",
01876       "Message: participant %s %s\r\n"
01877       "Conference: %s\r\n"
01878       "Channel: %s",
01879       ast_channel_name(user->chan),
01880       mute ? "muted" : "unmuted",
01881       user->b_profile.name,
01882       ast_channel_name(user->chan));
01883    if (mute) {
01884       send_mute_event(user, conference);
01885    } else {
01886       send_unmute_event(user, conference);
01887    }
01888 
01889    return play_file(bridge_channel, NULL, (mute ?
01890       conf_get_sound(CONF_SOUND_MUTED, user->b_profile.sounds) :
01891       conf_get_sound(CONF_SOUND_UNMUTED, user->b_profile.sounds))) < 0;
01892 }
01893 
01894 static int action_toggle_mute_participants(struct confbridge_conference *conference, struct confbridge_user *user)
01895 {
01896    struct confbridge_user *cur_user = NULL;
01897    const char *sound_to_play;
01898    int mute;
01899 
01900    ao2_lock(conference);
01901 
01902    /* Toggle bridge level mute request. */
01903    mute = !conference->muted;
01904    conference->muted = mute;
01905 
01906    AST_LIST_TRAVERSE(&conference->active_list, cur_user, list) {
01907       if (!ast_test_flag(&cur_user->u_profile, USER_OPT_ADMIN)) {
01908          /* Set user level to bridge level mute request. */
01909          cur_user->muted = mute;
01910          conf_update_user_mute(cur_user);
01911       }
01912    }
01913 
01914    ao2_unlock(conference);
01915 
01916    sound_to_play = conf_get_sound((mute ? CONF_SOUND_PARTICIPANTS_MUTED : CONF_SOUND_PARTICIPANTS_UNMUTED),
01917       user->b_profile.sounds);
01918 
01919    /* The host needs to hear it seperately, as they don't get the audio from play_sound_helper */
01920    ast_stream_and_wait(user->chan, sound_to_play, "");
01921 
01922    /* Announce to the group that all participants are muted */
01923    ast_autoservice_start(user->chan);
01924    play_sound_helper(conference, sound_to_play, 0);
01925    ast_autoservice_stop(user->chan);
01926 
01927    return 0;
01928 }
01929 
01930 static int action_playback(struct ast_bridge_channel *bridge_channel, const char *playback_file)
01931 {
01932    char *file_copy = ast_strdupa(playback_file);
01933    char *file = NULL;
01934 
01935    while ((file = strsep(&file_copy, "&"))) {
01936       if (ast_stream_and_wait(bridge_channel->chan, file, "")) {
01937          ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
01938          return -1;
01939       }
01940    }
01941    return 0;
01942 }
01943 
01944 static int action_playback_and_continue(struct confbridge_conference *conference,
01945    struct confbridge_user *user,
01946    struct ast_bridge_channel *bridge_channel,
01947    struct conf_menu *menu,
01948    const char *playback_file,
01949    const char *cur_dtmf,
01950    int *stop_prompts)
01951 {
01952    int i;
01953    int digit = 0;
01954    char dtmf[MAXIMUM_DTMF_FEATURE_STRING];
01955    struct conf_menu_entry new_menu_entry = { { 0, }, };
01956    char *file_copy = ast_strdupa(playback_file);
01957    char *file = NULL;
01958 
01959    while ((file = strsep(&file_copy, "&"))) {
01960       if (ast_streamfile(bridge_channel->chan, file, ast_channel_language(bridge_channel->chan))) {
01961          ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
01962          return -1;
01963       }
01964 
01965       /* now wait for more digits. */
01966       if (!(digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY))) {
01967          /* streaming finished and no DTMF was entered */
01968          continue;
01969       } else if (digit == -1) {
01970          /* error */
01971          return -1;
01972       } else {
01973          break; /* dtmf was entered */
01974       }
01975    }
01976    if (!digit) {
01977       /* streaming finished on all files and no DTMF was entered */
01978       return -1;
01979    }
01980    ast_stopstream(bridge_channel->chan);
01981 
01982    /* If we get here, then DTMF has been entered, This means no
01983     * additional prompts should be played for this menu entry */
01984    *stop_prompts = 1;
01985 
01986    /* If a digit was pressed during the payback, update
01987     * the dtmf string and look for a new menu entry in the
01988     * menu structure */
01989    ast_copy_string(dtmf, cur_dtmf, sizeof(dtmf));
01990    for (i = 0; i < (MAXIMUM_DTMF_FEATURE_STRING - 1); i++) {
01991       dtmf[i] = cur_dtmf[i];
01992       if (!dtmf[i]) {
01993          dtmf[i] = (char) digit;
01994          dtmf[i + 1] = '\0';
01995          i = -1;
01996          break;
01997       }
01998    }
01999    /* If i is not -1 then the new dtmf digit was _NOT_ added to the string.
02000     * If this is the case, no new DTMF sequence should be looked for. */
02001    if (i != -1) {
02002       return 0;
02003    }
02004 
02005    if (conf_find_menu_entry_by_sequence(dtmf, menu, &new_menu_entry)) {
02006       execute_menu_entry(conference,
02007          user,
02008          bridge_channel,
02009          &new_menu_entry, menu);
02010       conf_menu_entry_destroy(&new_menu_entry);
02011    }
02012    return 0;
02013 }
02014 
02015 static int action_kick_last(struct confbridge_conference *conference,
02016    struct ast_bridge_channel *bridge_channel,
02017    struct confbridge_user *user)
02018 {
02019    struct confbridge_user *last_user = NULL;
02020    int isadmin = ast_test_flag(&user->u_profile, USER_OPT_ADMIN);
02021 
02022    if (!isadmin) {
02023       play_file(bridge_channel, NULL,
02024            conf_get_sound(CONF_SOUND_ERROR_MENU, user->b_profile.sounds));
02025       ast_log(LOG_WARNING, "Only admin users can use the kick_last menu action. Channel %s of conf %s is not an admin.\n",
02026          ast_channel_name(bridge_channel->chan),
02027          conference->name);
02028       return -1;
02029    }
02030 
02031    ao2_lock(conference);
02032    if (((last_user = AST_LIST_LAST(&conference->active_list)) == user)
02033       || (ast_test_flag(&last_user->u_profile, USER_OPT_ADMIN))) {
02034       ao2_unlock(conference);
02035       play_file(bridge_channel, NULL,
02036            conf_get_sound(CONF_SOUND_ERROR_MENU, user->b_profile.sounds));
02037    } else if (last_user && !last_user->kicked) {
02038       last_user->kicked = 1;
02039       pbx_builtin_setvar_helper(last_user->chan, "CONFBRIDGE_RESULT", "KICKED");
02040       ast_bridge_remove(conference->bridge, last_user->chan);
02041       ao2_unlock(conference);
02042    }
02043    return 0;
02044 }
02045 
02046 static int action_dialplan_exec(struct ast_bridge_channel *bridge_channel, struct conf_menu_action *menu_action)
02047 {
02048    struct ast_pbx_args args;
02049    struct ast_pbx *pbx;
02050    char *exten;
02051    char *context;
02052    int priority;
02053    int res;
02054 
02055    memset(&args, 0, sizeof(args));
02056    args.no_hangup_chan = 1;
02057 
02058    ast_channel_lock(bridge_channel->chan);
02059 
02060    /*save off*/
02061    exten = ast_strdupa(ast_channel_exten(bridge_channel->chan));
02062    context = ast_strdupa(ast_channel_context(bridge_channel->chan));
02063    priority = ast_channel_priority(bridge_channel->chan);
02064    pbx = ast_channel_pbx(bridge_channel->chan);
02065    ast_channel_pbx_set(bridge_channel->chan, NULL);
02066 
02067    /*set new*/
02068    ast_channel_exten_set(bridge_channel->chan, menu_action->data.dialplan_args.exten);
02069    ast_channel_context_set(bridge_channel->chan, menu_action->data.dialplan_args.context);
02070    ast_channel_priority_set(bridge_channel->chan, menu_action->data.dialplan_args.priority);
02071 
02072    ast_channel_unlock(bridge_channel->chan);
02073 
02074    /*execute*/
02075    res = ast_pbx_run_args(bridge_channel->chan, &args);
02076 
02077    /*restore*/
02078    ast_channel_lock(bridge_channel->chan);
02079 
02080    ast_channel_exten_set(bridge_channel->chan, exten);
02081    ast_channel_context_set(bridge_channel->chan, context);
02082    ast_channel_priority_set(bridge_channel->chan, priority);
02083    ast_channel_pbx_set(bridge_channel->chan, pbx);
02084 
02085    ast_channel_unlock(bridge_channel->chan);
02086 
02087    return res;
02088 }
02089 
02090 static int execute_menu_entry(struct confbridge_conference *conference,
02091    struct confbridge_user *user,
02092    struct ast_bridge_channel *bridge_channel,
02093    struct conf_menu_entry *menu_entry,
02094    struct conf_menu *menu)
02095 {
02096    struct conf_menu_action *menu_action;
02097    int isadmin = ast_test_flag(&user->u_profile, USER_OPT_ADMIN);
02098    int stop_prompts = 0;
02099    int res = 0;
02100 
02101    AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
02102       switch (menu_action->id) {
02103       case MENU_ACTION_TOGGLE_MUTE:
02104          res |= action_toggle_mute(conference, user, bridge_channel);
02105          break;
02106       case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
02107          if (!isadmin) {
02108             break;
02109          }
02110          action_toggle_mute_participants(conference, user);
02111          break;
02112       case MENU_ACTION_PARTICIPANT_COUNT:
02113          announce_user_count(conference, user, bridge_channel);
02114          break;
02115       case MENU_ACTION_PLAYBACK:
02116          if (!stop_prompts) {
02117             res |= action_playback(bridge_channel, menu_action->data.playback_file);
02118             ast_test_suite_event_notify("CONF_MENU_PLAYBACK",
02119                "Message: %s\r\nChannel: %s",
02120                menu_action->data.playback_file, ast_channel_name(bridge_channel->chan));
02121          }
02122          break;
02123       case MENU_ACTION_RESET_LISTENING:
02124          ast_audiohook_volume_set(user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 0);
02125          break;
02126       case MENU_ACTION_RESET_TALKING:
02127          ast_audiohook_volume_set(user->chan, AST_AUDIOHOOK_DIRECTION_READ, 0);
02128          break;
02129       case MENU_ACTION_INCREASE_LISTENING:
02130          ast_audiohook_volume_adjust(user->chan,
02131             AST_AUDIOHOOK_DIRECTION_WRITE, 1);
02132          break;
02133       case MENU_ACTION_DECREASE_LISTENING:
02134          ast_audiohook_volume_adjust(user->chan,
02135             AST_AUDIOHOOK_DIRECTION_WRITE, -1);
02136          break;
02137       case MENU_ACTION_INCREASE_TALKING:
02138          ast_audiohook_volume_adjust(user->chan,
02139             AST_AUDIOHOOK_DIRECTION_READ, 1);
02140          break;
02141       case MENU_ACTION_DECREASE_TALKING:
02142          ast_audiohook_volume_adjust(user->chan,
02143             AST_AUDIOHOOK_DIRECTION_READ, -1);
02144          break;
02145       case MENU_ACTION_PLAYBACK_AND_CONTINUE:
02146          if (!(stop_prompts)) {
02147             res |= action_playback_and_continue(conference,
02148                user,
02149                bridge_channel,
02150                menu,
02151                menu_action->data.playback_file,
02152                menu_entry->dtmf,
02153                &stop_prompts);
02154          }
02155          break;
02156       case MENU_ACTION_DIALPLAN_EXEC:
02157          res |= action_dialplan_exec(bridge_channel, menu_action);
02158          break;
02159       case MENU_ACTION_ADMIN_TOGGLE_LOCK:
02160          if (!isadmin) {
02161             break;
02162          }
02163          conference->locked = (!conference->locked ? 1 : 0);
02164          res |= play_file(bridge_channel, NULL,
02165             (conference->locked ?
02166             conf_get_sound(CONF_SOUND_LOCKED_NOW, user->b_profile.sounds) :
02167             conf_get_sound(CONF_SOUND_UNLOCKED_NOW, user->b_profile.sounds))) < 0;
02168          break;
02169       case MENU_ACTION_ADMIN_KICK_LAST:
02170          res |= action_kick_last(conference, bridge_channel, user);
02171          break;
02172       case MENU_ACTION_LEAVE:
02173          pbx_builtin_setvar_helper(bridge_channel->chan, "CONFBRIDGE_RESULT", "DTMF");
02174          ao2_lock(conference);
02175          ast_bridge_remove(conference->bridge, bridge_channel->chan);
02176          ast_test_suite_event_notify("CONF_MENU_LEAVE",
02177             "Channel: %s",
02178             ast_channel_name(bridge_channel->chan));
02179          ao2_unlock(conference);
02180          break;
02181       case MENU_ACTION_NOOP:
02182          break;
02183       case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
02184          ao2_lock(conference);
02185          ast_bridge_set_single_src_video_mode(conference->bridge, bridge_channel->chan);
02186          ao2_unlock(conference);
02187          break;
02188       case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
02189          handle_video_on_exit(conference, bridge_channel->chan);
02190          break;
02191       }
02192    }
02193    return res;
02194 }
02195 
02196 int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel,
02197    struct confbridge_user *user,
02198    struct conf_menu_entry *menu_entry,
02199    struct conf_menu *menu)
02200 {
02201    /* See if music on hold is playing */
02202    conf_moh_suspend(user);
02203 
02204    /* execute the list of actions associated with this menu entry */
02205    execute_menu_entry(user->conference, user, bridge_channel, menu_entry, menu);
02206 
02207    /* See if music on hold needs to be started back up again */
02208    conf_moh_unsuspend(user);
02209 
02210    return 0;
02211 }
02212 
02213 static int kick_conference_participant(struct confbridge_conference *conference,
02214    const char *channel)
02215 {
02216    int res = -1;
02217    int match;
02218    struct confbridge_user *user = NULL;
02219    int all = !strcasecmp("all", channel);
02220    int participants = !strcasecmp("participants", channel);
02221 
02222    SCOPED_AO2LOCK(bridge_lock, conference);
02223 
02224    AST_LIST_TRAVERSE(&conference->active_list, user, list) {
02225       if (user->kicked) {
02226          continue;
02227       }
02228       match = !strcasecmp(channel, ast_channel_name(user->chan));
02229       if (match || all
02230             || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
02231          user->kicked = 1;
02232          pbx_builtin_setvar_helper(user->chan, "CONFBRIDGE_RESULT", "KICKED");
02233          ast_bridge_remove(conference->bridge, user->chan);
02234          res = 0;
02235          if (match) {
02236             return res;
02237          }
02238       }
02239    }
02240    AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
02241       if (user->kicked) {
02242          continue;
02243       }
02244       match = !strcasecmp(channel, ast_channel_name(user->chan));
02245       if (match || all
02246             || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
02247          user->kicked = 1;
02248          pbx_builtin_setvar_helper(user->chan, "CONFBRIDGE_RESULT", "KICKED");
02249          ast_bridge_remove(conference->bridge, user->chan);
02250          res = 0;
02251          if (match) {
02252             return res;
02253          }
02254       }
02255    }
02256 
02257    return res;
02258 }
02259 
02260 static char *complete_confbridge_name(const char *line, const char *word, int pos, int state)
02261 {
02262    int which = 0;
02263    struct confbridge_conference *conference;
02264    char *res = NULL;
02265    int wordlen = strlen(word);
02266    struct ao2_iterator iter;
02267 
02268    iter = ao2_iterator_init(conference_bridges, 0);
02269    while ((conference = ao2_iterator_next(&iter))) {
02270       if (!strncasecmp(conference->name, word, wordlen) && ++which > state) {
02271          res = ast_strdup(conference->name);
02272          ao2_ref(conference, -1);
02273          break;
02274       }
02275       ao2_ref(conference, -1);
02276    }
02277    ao2_iterator_destroy(&iter);
02278 
02279    return res;
02280 }
02281 
02282 static char *complete_confbridge_participant(const char *conference_name, const char *line, const char *word, int pos, int state)
02283 {
02284    int which = 0;
02285    RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup);
02286    struct confbridge_user *user;
02287    char *res = NULL;
02288    int wordlen = strlen(word);
02289 
02290    conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
02291    if (!conference) {
02292       return NULL;
02293    }
02294 
02295    if (!strncasecmp("all", word, wordlen) && ++which > state) {
02296       return ast_strdup("all");
02297    }
02298 
02299    if (!strncasecmp("participants", word, wordlen) && ++which > state) {
02300       return ast_strdup("participants");
02301    }
02302 
02303    {
02304       SCOPED_AO2LOCK(bridge_lock, conference);
02305       AST_LIST_TRAVERSE(&conference->active_list, user, list) {
02306          if (!strncasecmp(ast_channel_name(user->chan), word, wordlen) && ++which > state) {
02307             res = ast_strdup(ast_channel_name(user->chan));
02308             return res;
02309          }
02310       }
02311       AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
02312          if (!strncasecmp(ast_channel_name(user->chan), word, wordlen) && ++which > state) {
02313             res = ast_strdup(ast_channel_name(user->chan));
02314             return res;
02315          }
02316       }
02317    }
02318 
02319    return NULL;
02320 }
02321 
02322 static char *handle_cli_confbridge_kick(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02323 {
02324    struct confbridge_conference *conference;
02325    int not_found;
02326 
02327    switch (cmd) {
02328    case CLI_INIT:
02329       e->command = "confbridge kick";
02330       e->usage =
02331          "Usage: confbridge kick <conference> <channel>\n"
02332          "       Kicks a channel out of the conference bridge.\n"
02333          "             (all to kick everyone, participants to kick non-admins).\n";
02334       return NULL;
02335    case CLI_GENERATE:
02336       if (a->pos == 2) {
02337          return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02338       }
02339       if (a->pos == 3) {
02340          return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
02341       }
02342       return NULL;
02343    }
02344 
02345    if (a->argc != 4) {
02346       return CLI_SHOWUSAGE;
02347    }
02348 
02349    conference = ao2_find(conference_bridges, a->argv[2], OBJ_KEY);
02350    if (!conference) {
02351       ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
02352       return CLI_SUCCESS;
02353    }
02354    not_found = kick_conference_participant(conference, a->argv[3]);
02355    ao2_ref(conference, -1);
02356    if (not_found) {
02357       if (!strcasecmp("all", a->argv[3]) || !strcasecmp("participants", a->argv[3])) {
02358          ast_cli(a->fd, "No participants found!\n");
02359       } else {
02360          ast_cli(a->fd, "No participant named '%s' found!\n", a->argv[3]);
02361       }
02362       return CLI_SUCCESS;
02363    }
02364    ast_cli(a->fd, "Kicked '%s' out of conference '%s'\n", a->argv[3], a->argv[2]);
02365    return CLI_SUCCESS;
02366 }
02367 
02368 static void handle_cli_confbridge_list_item(struct ast_cli_args *a, struct confbridge_user *user, int waiting)
02369 {
02370    char flag_str[6 + 1];/* Max flags + terminator */
02371    int pos = 0;
02372 
02373    /* Build flags column string. */
02374    if (ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
02375       flag_str[pos++] = 'A';
02376    }
02377    if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
02378       flag_str[pos++] = 'M';
02379    }
02380    if (ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)) {
02381       flag_str[pos++] = 'W';
02382    }
02383    if (ast_test_flag(&user->u_profile, USER_OPT_ENDMARKED)) {
02384       flag_str[pos++] = 'E';
02385    }
02386    if (user->muted) {
02387       flag_str[pos++] = 'm';
02388    }
02389    if (waiting) {
02390       flag_str[pos++] = 'w';
02391    }
02392    flag_str[pos] = '\0';
02393 
02394    ast_cli(a->fd, "%-30s %-6s %-16s %-16s %-16s %s\n",
02395       ast_channel_name(user->chan),
02396       flag_str,
02397       user->u_profile.name,
02398       user->b_profile.name,
02399       user->menu_name,
02400       S_COR(ast_channel_caller(user->chan)->id.number.valid,
02401          ast_channel_caller(user->chan)->id.number.str, "<unknown>"));
02402 }
02403 
02404 static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02405 {
02406    struct confbridge_conference *conference;
02407 
02408    switch (cmd) {
02409    case CLI_INIT:
02410       e->command = "confbridge list";
02411       e->usage =
02412          "Usage: confbridge list [<name>]\n"
02413          "       Lists all currently active conference bridges or a specific conference bridge.\n"
02414          "\n"
02415          "       When a conference bridge name is provided, flags may be shown for users. Below\n"
02416          "       are the flags and what they represent.\n"
02417          "\n"
02418          "       Flags:\n"
02419          "         A - The user is an admin\n"
02420          "         M - The user is a marked user\n"
02421          "         W - The user must wait for a marked user to join\n"
02422          "         E - The user will be kicked after the last marked user leaves the conference\n"
02423          "         m - The user is muted\n"
02424          "         w - The user is waiting for a marked user to join\n";
02425       return NULL;
02426    case CLI_GENERATE:
02427       if (a->pos == 2) {
02428          return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02429       }
02430       return NULL;
02431    }
02432 
02433    if (a->argc == 2) {
02434       struct ao2_iterator iter;
02435 
02436       ast_cli(a->fd, "Conference Bridge Name           Users  Marked Locked?\n");
02437       ast_cli(a->fd, "================================ ====== ====== ========\n");
02438       iter = ao2_iterator_init(conference_bridges, 0);
02439       while ((conference = ao2_iterator_next(&iter))) {
02440          ast_cli(a->fd, "%-32s %6u %6u %s\n", conference->name, conference->activeusers + conference->waitingusers, conference->markedusers, (conference->locked ? "locked" : "unlocked"));
02441          ao2_ref(conference, -1);
02442       }
02443       ao2_iterator_destroy(&iter);
02444       return CLI_SUCCESS;
02445    }
02446 
02447    if (a->argc == 3) {
02448       struct confbridge_user *user;
02449 
02450       conference = ao2_find(conference_bridges, a->argv[2], OBJ_KEY);
02451       if (!conference) {
02452          ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
02453          return CLI_SUCCESS;
02454       }
02455       ast_cli(a->fd, "Channel                        Flags  User Profile     Bridge Profile   Menu             CallerID\n");
02456       ast_cli(a->fd, "============================== ====== ================ ================ ================ ================\n");
02457       ao2_lock(conference);
02458       AST_LIST_TRAVERSE(&conference->active_list, user, list) {
02459          handle_cli_confbridge_list_item(a, user, 0);
02460       }
02461       AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
02462          handle_cli_confbridge_list_item(a, user, 1);
02463       }
02464       ao2_unlock(conference);
02465       ao2_ref(conference, -1);
02466       return CLI_SUCCESS;
02467    }
02468 
02469    return CLI_SHOWUSAGE;
02470 }
02471 
02472 /* \internal
02473  * \brief finds a conference by name and locks/unlocks.
02474  *
02475  * \retval 0 success
02476  * \retval -1 conference not found
02477  */
02478 static int generic_lock_unlock_helper(int lock, const char *conference_name)
02479 {
02480    struct confbridge_conference *conference;
02481    int res = 0;
02482 
02483    conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
02484    if (!conference) {
02485       return -1;
02486    }
02487    ao2_lock(conference);
02488    conference->locked = lock;
02489    ast_test_suite_event_notify("CONF_LOCK", "Message: conference %s\r\nConference: %s", conference->locked ? "locked" : "unlocked", conference->b_profile.name);
02490    ao2_unlock(conference);
02491    ao2_ref(conference, -1);
02492 
02493    return res;
02494 }
02495 
02496 /* \internal
02497  * \brief Mute/unmute a single user.
02498  */
02499 static void generic_mute_unmute_user(struct confbridge_conference *conference, struct confbridge_user *user, int mute)
02500 {
02501    /* Set user level mute request. */
02502    user->muted = mute ? 1 : 0;
02503 
02504    conf_update_user_mute(user);
02505    ast_test_suite_event_notify("CONF_MUTE",
02506       "Message: participant %s %s\r\n"
02507       "Conference: %s\r\n"
02508       "Channel: %s",
02509       ast_channel_name(user->chan),
02510       mute ? "muted" : "unmuted",
02511       conference->b_profile.name,
02512       ast_channel_name(user->chan));
02513    if (mute) {
02514       send_mute_event(user, conference);
02515    } else {
02516       send_unmute_event(user, conference);
02517    }
02518 }
02519 
02520 /* \internal
02521  * \brief finds a conference user by channel name and mutes/unmutes them.
02522  *
02523  * \retval 0 success
02524  * \retval -1 conference not found
02525  * \retval -2 user not found
02526  */
02527 static int generic_mute_unmute_helper(int mute, const char *conference_name,
02528    const char *chan_name)
02529 {
02530    RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup);
02531    struct confbridge_user *user;
02532    int all = !strcasecmp("all", chan_name);
02533    int participants = !strcasecmp("participants", chan_name);
02534    int res = -2;
02535 
02536    conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
02537    if (!conference) {
02538       return -1;
02539    }
02540 
02541    {
02542       SCOPED_AO2LOCK(bridge_lock, conference);
02543       AST_LIST_TRAVERSE(&conference->active_list, user, list) {
02544          int match = !strncasecmp(chan_name, ast_channel_name(user->chan),
02545             strlen(chan_name));
02546          if (match || all
02547             || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
02548             generic_mute_unmute_user(conference, user, mute);
02549             res = 0;
02550             if (match) {
02551                return res;
02552             }
02553          }
02554       }
02555 
02556       AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
02557          int match = !strncasecmp(chan_name, ast_channel_name(user->chan),
02558             strlen(chan_name));
02559          if (match || all
02560             || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
02561             generic_mute_unmute_user(conference, user, mute);
02562             res = 0;
02563             if (match) {
02564                return res;
02565             }
02566          }
02567       }
02568    }
02569 
02570    return res;
02571 }
02572 
02573 static int cli_mute_unmute_helper(int mute, struct ast_cli_args *a)
02574 {
02575    int res = generic_mute_unmute_helper(mute, a->argv[2], a->argv[3]);
02576 
02577    if (res == -1) {
02578       ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
02579       return -1;
02580    } else if (res == -2) {
02581       if (!strcasecmp("all", a->argv[3]) || !strcasecmp("participants", a->argv[3])) {
02582          ast_cli(a->fd, "No participants found in conference %s\n", a->argv[2]);
02583       } else {
02584          ast_cli(a->fd, "No channel named '%s' found in conference %s\n", a->argv[3], a->argv[2]);
02585       }
02586       return -1;
02587    }
02588    ast_cli(a->fd, "%s %s from confbridge %s\n", mute ? "Muting" : "Unmuting", a->argv[3], a->argv[2]);
02589    return 0;
02590 }
02591 
02592 static char *handle_cli_confbridge_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02593 {
02594    switch (cmd) {
02595    case CLI_INIT:
02596       e->command = "confbridge mute";
02597       e->usage =
02598          "Usage: confbridge mute <conference> <channel>\n"
02599          "       Mute a channel in a conference.\n"
02600          "              (all to mute everyone, participants to mute non-admins)\n"
02601          "       If the specified channel is a prefix,\n"
02602          "       the action will be taken on the first\n"
02603          "       matching channel.\n";
02604       return NULL;
02605    case CLI_GENERATE:
02606       if (a->pos == 2) {
02607          return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02608       }
02609       if (a->pos == 3) {
02610          return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
02611       }
02612       return NULL;
02613    }
02614    if (a->argc != 4) {
02615       return CLI_SHOWUSAGE;
02616    }
02617 
02618    cli_mute_unmute_helper(1, a);
02619 
02620    return CLI_SUCCESS;
02621 }
02622 
02623 static char *handle_cli_confbridge_unmute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02624 {
02625    switch (cmd) {
02626    case CLI_INIT:
02627       e->command = "confbridge unmute";
02628       e->usage =
02629          "Usage: confbridge unmute <conference> <channel>\n"
02630          "       Unmute a channel in a conference.\n"
02631          "              (all to unmute everyone, participants to unmute non-admins)\n"
02632          "       If the specified channel is a prefix,\n"
02633          "       the action will be taken on the first\n"
02634          "       matching channel.\n";
02635       return NULL;
02636    case CLI_GENERATE:
02637       if (a->pos == 2) {
02638          return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02639       }
02640       if (a->pos == 3) {
02641          return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
02642       }
02643       return NULL;
02644    }
02645    if (a->argc != 4) {
02646       return CLI_SHOWUSAGE;
02647    }
02648 
02649    cli_mute_unmute_helper(0, a);
02650 
02651    return CLI_SUCCESS;
02652 }
02653 
02654 static char *handle_cli_confbridge_lock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02655 {
02656    switch (cmd) {
02657    case CLI_INIT:
02658       e->command = "confbridge lock";
02659       e->usage =
02660          "Usage: confbridge lock <conference>\n"
02661          "       Lock a conference. While locked, no new non-admins\n"
02662          "       may join the conference.\n";
02663       return NULL;
02664    case CLI_GENERATE:
02665       if (a->pos == 2) {
02666          return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02667       }
02668       return NULL;
02669    }
02670    if (a->argc != 3) {
02671       return CLI_SHOWUSAGE;
02672    }
02673    if (generic_lock_unlock_helper(1, a->argv[2])) {
02674       ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
02675    } else {
02676       ast_cli(a->fd, "Conference %s is locked.\n", a->argv[2]);
02677    }
02678    return CLI_SUCCESS;
02679 }
02680 
02681 static char *handle_cli_confbridge_unlock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02682 {
02683    switch (cmd) {
02684    case CLI_INIT:
02685       e->command = "confbridge unlock";
02686       e->usage =
02687          "Usage: confbridge unlock <conference>\n"
02688          "       Unlock a previously locked conference.\n";
02689       return NULL;
02690    case CLI_GENERATE:
02691       if (a->pos == 2) {
02692          return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02693       }
02694       return NULL;
02695    }
02696    if (a->argc != 3) {
02697       return CLI_SHOWUSAGE;
02698    }
02699    if (generic_lock_unlock_helper(0, a->argv[2])) {
02700       ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
02701    } else {
02702       ast_cli(a->fd, "Conference %s is unlocked.\n", a->argv[2]);
02703    }
02704    return CLI_SUCCESS;
02705 }
02706 
02707 static char *handle_cli_confbridge_start_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02708 {
02709    const char *rec_file = NULL;
02710    struct confbridge_conference *conference;
02711 
02712    switch (cmd) {
02713    case CLI_INIT:
02714       e->command = "confbridge record start";
02715       e->usage =
02716          "Usage: confbridge record start <conference> <file>\n"
02717          "       <file> is optional, Otherwise the bridge profile\n"
02718          "       record file will be used.  If the bridge profile\n"
02719          "       has no record file specified, a file will automatically\n"
02720          "       be generated in the monitor directory\n";
02721       return NULL;
02722    case CLI_GENERATE:
02723       if (a->pos == 3) {
02724          return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02725       }
02726       return NULL;
02727    }
02728    if (a->argc < 4) {
02729       return CLI_SHOWUSAGE;
02730    }
02731    if (a->argc == 5) {
02732       rec_file = a->argv[4];
02733    }
02734 
02735    conference = ao2_find(conference_bridges, a->argv[3], OBJ_KEY);
02736    if (!conference) {
02737       ast_cli(a->fd, "Conference not found.\n");
02738       return CLI_FAILURE;
02739    }
02740    ao2_lock(conference);
02741    if (conf_is_recording(conference)) {
02742       ast_cli(a->fd, "Conference is already being recorded.\n");
02743       ao2_unlock(conference);
02744       ao2_ref(conference, -1);
02745       return CLI_SUCCESS;
02746    }
02747    if (!ast_strlen_zero(rec_file)) {
02748       ast_copy_string(conference->b_profile.rec_file, rec_file, sizeof(conference->b_profile.rec_file));
02749    }
02750 
02751    if (conf_start_record(conference)) {
02752       ast_cli(a->fd, "Could not start recording due to internal error.\n");
02753       ao2_unlock(conference);
02754       ao2_ref(conference, -1);
02755       return CLI_FAILURE;
02756    }
02757    ao2_unlock(conference);
02758 
02759    ast_cli(a->fd, "Recording started\n");
02760    ao2_ref(conference, -1);
02761    return CLI_SUCCESS;
02762 }
02763 
02764 static char *handle_cli_confbridge_stop_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02765 {
02766    struct confbridge_conference *conference;
02767    int ret;
02768 
02769    switch (cmd) {
02770    case CLI_INIT:
02771       e->command = "confbridge record stop";
02772       e->usage =
02773          "Usage: confbridge record stop <conference>\n"
02774          "       Stop a previously started recording.\n";
02775       return NULL;
02776    case CLI_GENERATE:
02777       if (a->pos == 3) {
02778          return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02779       }
02780       return NULL;
02781    }
02782    if (a->argc != 4) {
02783       return CLI_SHOWUSAGE;
02784    }
02785 
02786    conference = ao2_find(conference_bridges, a->argv[3], OBJ_KEY);
02787    if (!conference) {
02788       ast_cli(a->fd, "Conference not found.\n");
02789       return CLI_SUCCESS;
02790    }
02791    ao2_lock(conference);
02792    ret = conf_stop_record(conference);
02793    ao2_unlock(conference);
02794    ast_cli(a->fd, "Recording %sstopped.\n", ret ? "could not be " : "");
02795    ao2_ref(conference, -1);
02796    return CLI_SUCCESS;
02797 }
02798 
02799 static struct ast_cli_entry cli_confbridge[] = {
02800    AST_CLI_DEFINE(handle_cli_confbridge_list, "List conference bridges and participants."),
02801    AST_CLI_DEFINE(handle_cli_confbridge_kick, "Kick participants out of conference bridges."),
02802    AST_CLI_DEFINE(handle_cli_confbridge_mute, "Mute participants."),
02803    AST_CLI_DEFINE(handle_cli_confbridge_unmute, "Unmute participants."),
02804    AST_CLI_DEFINE(handle_cli_confbridge_lock, "Lock a conference."),
02805    AST_CLI_DEFINE(handle_cli_confbridge_unlock, "Unlock a conference."),
02806    AST_CLI_DEFINE(handle_cli_confbridge_start_record, "Start recording a conference"),
02807    AST_CLI_DEFINE(handle_cli_confbridge_stop_record, "Stop recording a conference."),
02808 };
02809 static struct ast_custom_function confbridge_function = {
02810    .name = "CONFBRIDGE",
02811    .write = func_confbridge_helper,
02812 };
02813 
02814 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
02815 static struct ast_custom_function confbridge_info_function = {
02816    .name = "CONFBRIDGE_INFO",
02817    .read = func_confbridge_info,
02818 };
02819 
02820 static void action_confbridgelist_item(struct mansession *s, const char *id_text, struct confbridge_conference *conference, struct confbridge_user *user, int waiting)
02821 {
02822    astman_append(s,
02823       "Event: ConfbridgeList\r\n"
02824       "%s"
02825       "Conference: %s\r\n"
02826       "CallerIDNum: %s\r\n"
02827       "CallerIDName: %s\r\n"
02828       "Channel: %s\r\n"
02829       "Admin: %s\r\n"
02830       "MarkedUser: %s\r\n"
02831       "WaitMarked: %s\r\n"
02832       "EndMarked: %s\r\n"
02833       "Waiting: %s\r\n"
02834       "Muted: %s\r\n"
02835       "AnsweredTime: %d\r\n"
02836       "\r\n",
02837       id_text,
02838       conference->name,
02839       S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
02840       S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<no name>"),
02841       ast_channel_name(user->chan),
02842       ast_test_flag(&user->u_profile, USER_OPT_ADMIN) ? "Yes" : "No",
02843       ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER) ? "Yes" : "No",
02844       ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED) ? "Yes" : "No",
02845       ast_test_flag(&user->u_profile, USER_OPT_ENDMARKED) ? "Yes" : "No",
02846       waiting ? "Yes" : "No",
02847       user->muted ? "Yes" : "No",
02848       ast_channel_get_up_time(user->chan));
02849 }
02850 
02851 static int action_confbridgelist(struct mansession *s, const struct message *m)
02852 {
02853    const char *actionid = astman_get_header(m, "ActionID");
02854    const char *conference_name = astman_get_header(m, "Conference");
02855    struct confbridge_user *user;
02856    struct confbridge_conference *conference;
02857    char id_text[80];
02858    int total = 0;
02859 
02860    id_text[0] = '\0';
02861    if (!ast_strlen_zero(actionid)) {
02862       snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
02863    }
02864    if (ast_strlen_zero(conference_name)) {
02865       astman_send_error(s, m, "No Conference name provided.");
02866       return 0;
02867    }
02868    if (!ao2_container_count(conference_bridges)) {
02869       astman_send_error(s, m, "No active conferences.");
02870       return 0;
02871    }
02872    conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
02873    if (!conference) {
02874       astman_send_error(s, m, "No Conference by that name found.");
02875       return 0;
02876    }
02877 
02878    astman_send_listack(s, m, "Confbridge user list will follow", "start");
02879 
02880    ao2_lock(conference);
02881    AST_LIST_TRAVERSE(&conference->active_list, user, list) {
02882       total++;
02883       action_confbridgelist_item(s, id_text, conference, user, 0);
02884    }
02885    AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
02886       total++;
02887       action_confbridgelist_item(s, id_text, conference, user, 1);
02888    }
02889    ao2_unlock(conference);
02890    ao2_ref(conference, -1);
02891 
02892    astman_send_list_complete_start(s, m, "ConfbridgeListComplete", total);
02893    astman_send_list_complete_end(s);
02894 
02895    return 0;
02896 }
02897 
02898 static int action_confbridgelistrooms(struct mansession *s, const struct message *m)
02899 {
02900    const char *actionid = astman_get_header(m, "ActionID");
02901    struct confbridge_conference *conference;
02902    struct ao2_iterator iter;
02903    char id_text[512] = "";
02904    int totalitems = 0;
02905 
02906    if (!ast_strlen_zero(actionid)) {
02907       snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
02908    }
02909 
02910    if (!ao2_container_count(conference_bridges)) {
02911       astman_send_error(s, m, "No active conferences.");
02912       return 0;
02913    }
02914 
02915    astman_send_listack(s, m, "Confbridge conferences will follow", "start");
02916 
02917    /* Traverse the conference list */
02918    iter = ao2_iterator_init(conference_bridges, 0);
02919    while ((conference = ao2_iterator_next(&iter))) {
02920       totalitems++;
02921 
02922       ao2_lock(conference);
02923       astman_append(s,
02924       "Event: ConfbridgeListRooms\r\n"
02925       "%s"
02926       "Conference: %s\r\n"
02927       "Parties: %u\r\n"
02928       "Marked: %u\r\n"
02929       "Locked: %s\r\n"
02930       "\r\n",
02931       id_text,
02932       conference->name,
02933       conference->activeusers + conference->waitingusers,
02934       conference->markedusers,
02935       conference->locked ? "Yes" : "No");
02936       ao2_unlock(conference);
02937 
02938       ao2_ref(conference, -1);
02939    }
02940    ao2_iterator_destroy(&iter);
02941 
02942    /* Send final confirmation */
02943    astman_send_list_complete_start(s, m, "ConfbridgeListRoomsComplete", totalitems);
02944    astman_send_list_complete_end(s);
02945    return 0;
02946 }
02947 
02948 static int action_mute_unmute_helper(struct mansession *s, const struct message *m, int mute)
02949 {
02950    const char *conference_name = astman_get_header(m, "Conference");
02951    const char *channel_name = astman_get_header(m, "Channel");
02952    int res = 0;
02953 
02954    if (ast_strlen_zero(conference_name)) {
02955       astman_send_error(s, m, "No Conference name provided.");
02956       return 0;
02957    }
02958    if (ast_strlen_zero(channel_name)) {
02959       astman_send_error(s, m, "No channel name provided.");
02960       return 0;
02961    }
02962    if (!ao2_container_count(conference_bridges)) {
02963       astman_send_error(s, m, "No active conferences.");
02964       return 0;
02965    }
02966 
02967    res = generic_mute_unmute_helper(mute, conference_name, channel_name);
02968 
02969    if (res == -1) {
02970       astman_send_error(s, m, "No Conference by that name found.");
02971       return 0;
02972    } else if (res == -2) {
02973       astman_send_error(s, m, "No Channel by that name found in Conference.");
02974       return 0;
02975    }
02976 
02977    astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
02978    return 0;
02979 }
02980 
02981 static int action_confbridgeunmute(struct mansession *s, const struct message *m)
02982 {
02983    return action_mute_unmute_helper(s, m, 0);
02984 }
02985 static int action_confbridgemute(struct mansession *s, const struct message *m)
02986 {
02987    return action_mute_unmute_helper(s, m, 1);
02988 }
02989 
02990 static int action_lock_unlock_helper(struct mansession *s, const struct message *m, int lock)
02991 {
02992    const char *conference_name = astman_get_header(m, "Conference");
02993    int res = 0;
02994 
02995    if (ast_strlen_zero(conference_name)) {
02996       astman_send_error(s, m, "No Conference name provided.");
02997       return 0;
02998    }
02999    if (!ao2_container_count(conference_bridges)) {
03000       astman_send_error(s, m, "No active conferences.");
03001       return 0;
03002    }
03003    if ((res = generic_lock_unlock_helper(lock, conference_name))) {
03004       astman_send_error(s, m, "No Conference by that name found.");
03005       return 0;
03006    }
03007    astman_send_ack(s, m, lock ? "Conference locked" : "Conference unlocked");
03008    return 0;
03009 }
03010 static int action_confbridgeunlock(struct mansession *s, const struct message *m)
03011 {
03012    return action_lock_unlock_helper(s, m, 0);
03013 }
03014 static int action_confbridgelock(struct mansession *s, const struct message *m)
03015 {
03016    return action_lock_unlock_helper(s, m, 1);
03017 }
03018 
03019 static int action_confbridgekick(struct mansession *s, const struct message *m)
03020 {
03021    const char *conference_name = astman_get_header(m, "Conference");
03022    const char *channel = astman_get_header(m, "Channel");
03023    struct confbridge_conference *conference;
03024    int found;
03025 
03026    if (ast_strlen_zero(conference_name)) {
03027       astman_send_error(s, m, "No Conference name provided.");
03028       return 0;
03029    }
03030    if (!ao2_container_count(conference_bridges)) {
03031       astman_send_error(s, m, "No active conferences.");
03032       return 0;
03033    }
03034 
03035    conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
03036    if (!conference) {
03037       astman_send_error(s, m, "No Conference by that name found.");
03038       return 0;
03039    }
03040 
03041    found = !kick_conference_participant(conference, channel);
03042    ao2_ref(conference, -1);
03043 
03044    if (found) {
03045       astman_send_ack(s, m, !strcmp("all", channel) ? "All participants kicked" : "User kicked");
03046    } else {
03047       astman_send_error(s, m, "No Channel by that name found in Conference.");
03048    }
03049    return 0;
03050 }
03051 
03052 static int action_confbridgestartrecord(struct mansession *s, const struct message *m)
03053 {
03054    const char *conference_name = astman_get_header(m, "Conference");
03055    const char *recordfile = astman_get_header(m, "RecordFile");
03056    struct confbridge_conference *conference;
03057 
03058    if (ast_strlen_zero(conference_name)) {
03059       astman_send_error(s, m, "No Conference name provided.");
03060       return 0;
03061    }
03062    if (!ao2_container_count(conference_bridges)) {
03063       astman_send_error(s, m, "No active conferences.");
03064       return 0;
03065    }
03066 
03067    conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
03068    if (!conference) {
03069       astman_send_error(s, m, "No Conference by that name found.");
03070       return 0;
03071    }
03072 
03073    ao2_lock(conference);
03074    if (conf_is_recording(conference)) {
03075       astman_send_error(s, m, "Conference is already being recorded.");
03076       ao2_unlock(conference);
03077       ao2_ref(conference, -1);
03078       return 0;
03079    }
03080 
03081    if (!ast_strlen_zero(recordfile)) {
03082       ast_copy_string(conference->b_profile.rec_file, recordfile, sizeof(conference->b_profile.rec_file));
03083    }
03084 
03085    if (conf_start_record(conference)) {
03086       astman_send_error(s, m, "Internal error starting conference recording.");
03087       ao2_unlock(conference);
03088       ao2_ref(conference, -1);
03089       return 0;
03090    }
03091    ao2_unlock(conference);
03092 
03093    ao2_ref(conference, -1);
03094    astman_send_ack(s, m, "Conference Recording Started.");
03095    return 0;
03096 }
03097 static int action_confbridgestoprecord(struct mansession *s, const struct message *m)
03098 {
03099    const char *conference_name = astman_get_header(m, "Conference");
03100    struct confbridge_conference *conference;
03101 
03102    if (ast_strlen_zero(conference_name)) {
03103       astman_send_error(s, m, "No Conference name provided.");
03104       return 0;
03105    }
03106    if (!ao2_container_count(conference_bridges)) {
03107       astman_send_error(s, m, "No active conferences.");
03108       return 0;
03109    }
03110 
03111    conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
03112    if (!conference) {
03113       astman_send_error(s, m, "No Conference by that name found.");
03114       return 0;
03115    }
03116 
03117    ao2_lock(conference);
03118    if (conf_stop_record(conference)) {
03119       ao2_unlock(conference);
03120       astman_send_error(s, m, "Internal error while stopping recording.");
03121       ao2_ref(conference, -1);
03122       return 0;
03123    }
03124    ao2_unlock(conference);
03125 
03126    ao2_ref(conference, -1);
03127    astman_send_ack(s, m, "Conference Recording Stopped.");
03128    return 0;
03129 }
03130 
03131 static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m)
03132 {
03133    const char *conference_name = astman_get_header(m, "Conference");
03134    const char *channel = astman_get_header(m, "Channel");
03135    struct confbridge_user *user;
03136    struct confbridge_conference *conference;
03137 
03138    if (ast_strlen_zero(conference_name)) {
03139       astman_send_error(s, m, "No Conference name provided.");
03140       return 0;
03141    }
03142    if (ast_strlen_zero(channel)) {
03143       astman_send_error(s, m, "No channel name provided.");
03144       return 0;
03145    }
03146    if (!ao2_container_count(conference_bridges)) {
03147       astman_send_error(s, m, "No active conferences.");
03148       return 0;
03149    }
03150 
03151    conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
03152    if (!conference) {
03153       astman_send_error(s, m, "No Conference by that name found.");
03154       return 0;
03155    }
03156 
03157    /* find channel and set as video src. */
03158    ao2_lock(conference);
03159    AST_LIST_TRAVERSE(&conference->active_list, user, list) {
03160       if (!strncmp(channel, ast_channel_name(user->chan), strlen(channel))) {
03161          ast_bridge_set_single_src_video_mode(conference->bridge, user->chan);
03162          break;
03163       }
03164    }
03165    ao2_unlock(conference);
03166    ao2_ref(conference, -1);
03167 
03168    /* do not access user after conference unlock.  We are just
03169     * using this check to see if it was found or not */
03170    if (!user) {
03171       astman_send_error(s, m, "No channel by that name found in conference.");
03172       return 0;
03173    }
03174    astman_send_ack(s, m, "Conference single video source set.");
03175    return 0;
03176 }
03177 
03178 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
03179 {
03180    char *parse;
03181    struct confbridge_conference *conference;
03182    struct confbridge_user *user;
03183    int count = 0;
03184    AST_DECLARE_APP_ARGS(args,
03185       AST_APP_ARG(type);
03186       AST_APP_ARG(confno);
03187    );
03188 
03189    /* parse all the required arguments and make sure they exist. */
03190    if (ast_strlen_zero(data)) {
03191       return -1;
03192    }
03193    parse = ast_strdupa(data);
03194    AST_STANDARD_APP_ARGS(args, parse);
03195    if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
03196       return -1;
03197    }
03198    conference = ao2_find(conference_bridges, args.confno, OBJ_KEY);
03199    if (!conference) {
03200       snprintf(buf, len, "0");
03201       return 0;
03202    }
03203 
03204    /* get the correct count for the type requested */
03205    ao2_lock(conference);
03206    if (!strncasecmp(args.type, "parties", 7)) {
03207       AST_LIST_TRAVERSE(&conference->active_list, user, list) {
03208          count++;
03209       }
03210       AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
03211          count++;
03212       }
03213    } else if (!strncasecmp(args.type, "admins", 6)) {
03214       AST_LIST_TRAVERSE(&conference->active_list, user, list) {
03215          if (ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
03216             count++;
03217          }
03218       }
03219    } else if (!strncasecmp(args.type, "marked", 6)) {
03220       AST_LIST_TRAVERSE(&conference->active_list, user, list) {
03221          if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
03222             count++;
03223          }
03224       }
03225    } else if (!strncasecmp(args.type, "locked", 6)) {
03226       count = conference->locked;
03227    } else {
03228       ast_log(LOG_ERROR, "Invalid keyword '%s' passed to CONFBRIDGE_INFO.  Should be one of: "
03229          "parties, admins, marked, or locked.\n", args.type);
03230    }
03231    snprintf(buf, len, "%d", count);
03232    ao2_unlock(conference);
03233    ao2_ref(conference, -1);
03234    return 0;
03235 }
03236 
03237 void conf_add_user_active(struct confbridge_conference *conference, struct confbridge_user *user)
03238 {
03239    AST_LIST_INSERT_TAIL(&conference->active_list, user, list);
03240    conference->activeusers++;
03241 }
03242 
03243 void conf_add_user_marked(struct confbridge_conference *conference, struct confbridge_user *user)
03244 {
03245    AST_LIST_INSERT_TAIL(&conference->active_list, user, list);
03246    conference->activeusers++;
03247    conference->markedusers++;
03248 }
03249 
03250 void conf_add_user_waiting(struct confbridge_conference *conference, struct confbridge_user *user)
03251 {
03252    AST_LIST_INSERT_TAIL(&conference->waiting_list, user, list);
03253    conference->waitingusers++;
03254 }
03255 
03256 void conf_remove_user_active(struct confbridge_conference *conference, struct confbridge_user *user)
03257 {
03258    AST_LIST_REMOVE(&conference->active_list, user, list);
03259    conference->activeusers--;
03260 }
03261 
03262 void conf_remove_user_marked(struct confbridge_conference *conference, struct confbridge_user *user)
03263 {
03264    AST_LIST_REMOVE(&conference->active_list, user, list);
03265    conference->activeusers--;
03266    conference->markedusers--;
03267 }
03268 
03269 void conf_mute_only_active(struct confbridge_conference *conference)
03270 {
03271    struct confbridge_user *only_user = AST_LIST_FIRST(&conference->active_list);
03272 
03273    /* Turn on MOH if the single participant is set up for it */
03274    if (ast_test_flag(&only_user->u_profile, USER_OPT_MUSICONHOLD)) {
03275       conf_moh_start(only_user);
03276    }
03277    conf_update_user_mute(only_user);
03278 }
03279 
03280 void conf_remove_user_waiting(struct confbridge_conference *conference, struct confbridge_user *user)
03281 {
03282    AST_LIST_REMOVE(&conference->waiting_list, user, list);
03283    conference->waitingusers--;
03284 }
03285 
03286 /*!
03287  * \internal
03288  * \brief Unregister a ConfBridge channel technology.
03289  * \since 12.0.0
03290  *
03291  * \param tech What to unregister.
03292  *
03293  * \return Nothing
03294  */
03295 static void unregister_channel_tech(struct ast_channel_tech *tech)
03296 {
03297    ast_channel_unregister(tech);
03298    ao2_cleanup(tech->capabilities);
03299 }
03300 
03301 /*!
03302  * \internal
03303  * \brief Register a ConfBridge channel technology.
03304  * \since 12.0.0
03305  *
03306  * \param tech What to register.
03307  *
03308  * \retval 0 on success.
03309  * \retval -1 on error.
03310  */
03311 static int register_channel_tech(struct ast_channel_tech *tech)
03312 {
03313    tech->capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
03314    if (!tech->capabilities) {
03315       return -1;
03316    }
03317    ast_format_cap_append_by_type(tech->capabilities, AST_MEDIA_TYPE_UNKNOWN);
03318    if (ast_channel_register(tech)) {
03319       ast_log(LOG_ERROR, "Unable to register channel technology %s(%s).\n",
03320          tech->type, tech->description);
03321       return -1;
03322    }
03323    return 0;
03324 }
03325 
03326 /*! \brief Called when module is being unloaded */
03327 static int unload_module(void)
03328 {
03329    ast_unregister_application(app);
03330 
03331    ast_custom_function_unregister(&confbridge_function);
03332    ast_custom_function_unregister(&confbridge_info_function);
03333 
03334    ast_cli_unregister_multiple(cli_confbridge, ARRAY_LEN(cli_confbridge));
03335 
03336    ast_manager_unregister("ConfbridgeList");
03337    ast_manager_unregister("ConfbridgeListRooms");
03338    ast_manager_unregister("ConfbridgeMute");
03339    ast_manager_unregister("ConfbridgeUnmute");
03340    ast_manager_unregister("ConfbridgeKick");
03341    ast_manager_unregister("ConfbridgeUnlock");
03342    ast_manager_unregister("ConfbridgeLock");
03343    ast_manager_unregister("ConfbridgeStartRecord");
03344    ast_manager_unregister("ConfbridgeStopRecord");
03345    ast_manager_unregister("ConfbridgeSetSingleVideoSrc");
03346 
03347    /* Unsubscribe from stasis confbridge message type and clean it up. */
03348    manager_confbridge_shutdown();
03349 
03350    /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
03351    ao2_cleanup(conference_bridges);
03352    conference_bridges = NULL;
03353 
03354    conf_destroy_config();
03355 
03356    unregister_channel_tech(conf_announce_get_tech());
03357    unregister_channel_tech(conf_record_get_tech());
03358 
03359    return 0;
03360 }
03361 
03362 /*!
03363  * \brief Load the module
03364  *
03365  * Module loading including tests for configuration or dependencies.
03366  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
03367  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
03368  * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
03369  * configuration file or other non-critical problem return
03370  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
03371  */
03372 static int load_module(void)
03373 {
03374    int res = 0;
03375 
03376    if (conf_load_config()) {
03377       ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
03378       return AST_MODULE_LOAD_DECLINE;
03379    }
03380 
03381    if (register_channel_tech(conf_record_get_tech())
03382       || register_channel_tech(conf_announce_get_tech())) {
03383       unload_module();
03384       return AST_MODULE_LOAD_FAILURE;
03385    }
03386 
03387    /* Create a container to hold the conference bridges */
03388    conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS,
03389       conference_bridge_hash_cb, conference_bridge_cmp_cb);
03390    if (!conference_bridges) {
03391       unload_module();
03392       return AST_MODULE_LOAD_FAILURE;
03393    }
03394 
03395    /* Setup manager stasis subscriptions */
03396    res |= manager_confbridge_init();
03397 
03398    res |= ast_register_application_xml(app, confbridge_exec);
03399 
03400    res |= ast_custom_function_register_escalating(&confbridge_function, AST_CFE_WRITE);
03401    res |= ast_custom_function_register(&confbridge_info_function);
03402 
03403    res |= ast_cli_register_multiple(cli_confbridge, ARRAY_LEN(cli_confbridge));
03404 
03405    res |= ast_manager_register_xml("ConfbridgeList", EVENT_FLAG_REPORTING, action_confbridgelist);
03406    res |= ast_manager_register_xml("ConfbridgeListRooms", EVENT_FLAG_REPORTING, action_confbridgelistrooms);
03407    res |= ast_manager_register_xml("ConfbridgeMute", EVENT_FLAG_CALL, action_confbridgemute);
03408    res |= ast_manager_register_xml("ConfbridgeUnmute", EVENT_FLAG_CALL, action_confbridgeunmute);
03409    res |= ast_manager_register_xml("ConfbridgeKick", EVENT_FLAG_CALL, action_confbridgekick);
03410    res |= ast_manager_register_xml("ConfbridgeUnlock", EVENT_FLAG_CALL, action_confbridgeunlock);
03411    res |= ast_manager_register_xml("ConfbridgeLock", EVENT_FLAG_CALL, action_confbridgelock);
03412    res |= ast_manager_register_xml("ConfbridgeStartRecord", EVENT_FLAG_SYSTEM, action_confbridgestartrecord);
03413    res |= ast_manager_register_xml("ConfbridgeStopRecord", EVENT_FLAG_SYSTEM, action_confbridgestoprecord);
03414    res |= ast_manager_register_xml("ConfbridgeSetSingleVideoSrc", EVENT_FLAG_CALL, action_confbridgesetsinglevideosrc);
03415    if (res) {
03416       unload_module();
03417       return AST_MODULE_LOAD_FAILURE;
03418    }
03419 
03420    return AST_MODULE_LOAD_SUCCESS;
03421 }
03422 
03423 static int reload(void)
03424 {
03425    return conf_reload_config();
03426 }
03427 
03428 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Conference Bridge Application",
03429    .support_level = AST_MODULE_SUPPORT_CORE,
03430    .load = load_module,
03431    .unload = unload_module,
03432    .reload = reload,
03433    .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
03434 );

Generated on Thu Apr 16 06:27:08 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6