Sat Jul 12 2014 17:18:23

Asterisk developer's documentation


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