Sat Apr 26 2014 22:01:26

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: 410965 $")
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    if (ast_strlen_zero(data)) {
01591       ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
01592       res = -1;
01593       goto confbridge_cleanup;
01594    }
01595 
01596    /* We need to make a copy of the input string if we are going to modify it! */
01597    parse = ast_strdupa(data);
01598 
01599    AST_STANDARD_APP_ARGS(args, parse);
01600 
01601    /* bridge profile name */
01602    if (args.argc > 1 && !ast_strlen_zero(args.b_profile_name)) {
01603       b_profile_name = args.b_profile_name;
01604    }
01605    if (!conf_find_bridge_profile(chan, b_profile_name, &conference_bridge_user.b_profile)) {
01606       ast_log(LOG_WARNING, "Conference bridge profile %s does not exist\n", b_profile_name);
01607       res = -1;
01608       goto confbridge_cleanup;
01609    }
01610 
01611    /* user profile name */
01612    if (args.argc > 2 && !ast_strlen_zero(args.u_profile_name)) {
01613       u_profile_name = args.u_profile_name;
01614    }
01615    if (!conf_find_user_profile(chan, u_profile_name, &conference_bridge_user.u_profile)) {
01616       ast_log(LOG_WARNING, "Conference user profile %s does not exist\n", u_profile_name);
01617       res = -1;
01618       goto confbridge_cleanup;
01619    }
01620 
01621    quiet = ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_QUIET);
01622 
01623    /* ask for a PIN immediately after finding user profile.  This has to be
01624     * prompted for requardless of quiet setting. */
01625    if (!ast_strlen_zero(conference_bridge_user.u_profile.pin)) {
01626       if (conf_get_pin(chan, &conference_bridge_user)) {
01627          res = -1; /* invalid PIN */
01628          goto confbridge_cleanup;
01629       }
01630    }
01631 
01632    /* See if we need them to record a intro name */
01633    if (!quiet && ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE)) {
01634       conf_rec_name(&conference_bridge_user, args.conf_name);
01635    }
01636 
01637    /* menu name */
01638    if (args.argc > 3 && !ast_strlen_zero(args.menu_name)) {
01639       ast_copy_string(conference_bridge_user.menu_name, args.menu_name, sizeof(conference_bridge_user.menu_name));
01640       if (conf_set_menu_to_user(conference_bridge_user.menu_name, &conference_bridge_user)) {
01641          ast_log(LOG_WARNING, "Conference menu %s does not exist and can not be applied to confbridge user.\n",
01642             args.menu_name);
01643          res = -1;
01644          goto confbridge_cleanup;
01645       }
01646    }
01647 
01648    /* Set if DTMF should pass through for this user or not */
01649    if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DTMF_PASS)) {
01650       conference_bridge_user.features.dtmf_passthrough = 1;
01651    }
01652 
01653    /* Set dsp threshold values if present */
01654    if (conference_bridge_user.u_profile.talking_threshold) {
01655       conference_bridge_user.tech_args.talking_threshold = conference_bridge_user.u_profile.talking_threshold;
01656    }
01657    if (conference_bridge_user.u_profile.silence_threshold) {
01658       conference_bridge_user.tech_args.silence_threshold = conference_bridge_user.u_profile.silence_threshold;
01659    }
01660 
01661    /* Set a talker indicate call back if talking detection is requested */
01662    if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_TALKER_DETECT)) {
01663       char *conf_name = ast_strdup(args.conf_name); /* this is freed during feature cleanup */
01664       if (!(conf_name)) {
01665          res = -1;
01666          goto confbridge_cleanup;
01667       }
01668       ast_bridge_features_set_talk_detector(&conference_bridge_user.features,
01669          conf_handle_talker_cb,
01670          conf_handle_talker_destructor,
01671          conf_name);
01672    }
01673 
01674    /* If the caller should be joined already muted, set the flag before we join. */
01675    if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_STARTMUTED)) {
01676       /* Set user level mute request. */
01677       conference_bridge_user.muted = 1;
01678    }
01679 
01680    /* Look for a conference bridge matching the provided name */
01681    if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) {
01682       res = -1;
01683       goto confbridge_cleanup;
01684    }
01685 
01686    /* Keep a copy of volume adjustments so we can restore them later if need be */
01687    volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
01688    volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
01689 
01690    if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DROP_SILENCE)) {
01691       conference_bridge_user.tech_args.drop_silence = 1;
01692    }
01693 
01694    if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_JITTERBUFFER)) {
01695       char *func_jb;
01696       if ((func_jb = ast_module_helper("", "func_jitterbuffer", 0, 0, 0, 0))) {
01697          ast_free(func_jb);
01698          ast_func_write(chan, "JITTERBUFFER(adaptive)", "default");
01699       }
01700    }
01701 
01702    if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DENOISE)) {
01703       char *mod_speex;
01704       /* Reduce background noise from each participant */
01705       if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
01706          ast_free(mod_speex);
01707          ast_func_write(chan, "DENOISE(rx)", "on");
01708       }
01709    }
01710 
01711    /* if this user has a intro, play it before entering */
01712    if (!ast_strlen_zero(conference_bridge_user.name_rec_location)) {
01713       ast_autoservice_start(chan);
01714       play_sound_file(conference_bridge, conference_bridge_user.name_rec_location);
01715       play_sound_file(conference_bridge,
01716          conf_get_sound(CONF_SOUND_HAS_JOINED, conference_bridge_user.b_profile.sounds));
01717       ast_autoservice_stop(chan);
01718    }
01719 
01720    /* Play the Join sound to both the conference and the user entering. */
01721    if (!quiet) {
01722       const char *join_sound = conf_get_sound(CONF_SOUND_JOIN, conference_bridge_user.b_profile.sounds);
01723 
01724       ast_stream_and_wait(chan, join_sound, "");
01725       ast_autoservice_start(chan);
01726       play_sound_file(conference_bridge, join_sound);
01727       ast_autoservice_stop(chan);
01728    }
01729 
01730    /* See if we need to automatically set this user as a video source or not */
01731    handle_video_on_join(conference_bridge, conference_bridge_user.chan, ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_MARKEDUSER));
01732 
01733    conf_moh_unsuspend(&conference_bridge_user);
01734 
01735    /* Join our conference bridge for real */
01736    send_join_event(conference_bridge_user.chan, conference_bridge->name);
01737    ast_bridge_join(conference_bridge->bridge,
01738       chan,
01739       NULL,
01740       &conference_bridge_user.features,
01741       &conference_bridge_user.tech_args);
01742    send_leave_event(conference_bridge_user.chan, conference_bridge->name);
01743 
01744    /* if we're shutting down, don't attempt to do further processing */
01745    if (ast_shutting_down()) {
01746       leave_conference(&conference_bridge_user);
01747       conference_bridge = NULL;
01748       goto confbridge_cleanup;
01749    }
01750 
01751    /* If this user was a video source, we need to clean up and possibly pick a new source. */
01752    handle_video_on_exit(conference_bridge, conference_bridge_user.chan);
01753 
01754    /* if this user has a intro, play it when leaving */
01755    if (!quiet && !ast_strlen_zero(conference_bridge_user.name_rec_location)) {
01756       ast_autoservice_start(chan);
01757       play_sound_file(conference_bridge, conference_bridge_user.name_rec_location);
01758       play_sound_file(conference_bridge,
01759          conf_get_sound(CONF_SOUND_HAS_LEFT, conference_bridge_user.b_profile.sounds));
01760       ast_autoservice_stop(chan);
01761    }
01762 
01763    /* play the leave sound */
01764    if (!quiet) {
01765       const char *leave_sound = conf_get_sound(CONF_SOUND_LEAVE, conference_bridge_user.b_profile.sounds);
01766       ast_autoservice_start(chan);
01767       play_sound_file(conference_bridge, leave_sound);
01768       ast_autoservice_stop(chan);
01769    }
01770 
01771    /* Easy as pie, depart this channel from the conference bridge */
01772    leave_conference(&conference_bridge_user);
01773    conference_bridge = NULL;
01774 
01775    /* If the user was kicked from the conference play back the audio prompt for it */
01776    if (!quiet && conference_bridge_user.kicked) {
01777       res = ast_stream_and_wait(chan,
01778          conf_get_sound(CONF_SOUND_KICKED, conference_bridge_user.b_profile.sounds),
01779          "");
01780    }
01781 
01782    /* Restore volume adjustments to previous values in case they were changed */
01783    if (volume_adjustments[0]) {
01784       ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
01785    }
01786    if (volume_adjustments[1]) {
01787       ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
01788    }
01789 
01790    if (!ast_strlen_zero(conference_bridge_user.name_rec_location)) {
01791       ast_filedelete(conference_bridge_user.name_rec_location, NULL);
01792    }
01793 
01794 confbridge_cleanup:
01795    ast_bridge_features_cleanup(&conference_bridge_user.features);
01796    conf_bridge_profile_destroy(&conference_bridge_user.b_profile);
01797    return res;
01798 }
01799 
01800 static int action_toggle_mute(struct conference_bridge *conference_bridge,
01801    struct conference_bridge_user *conference_bridge_user,
01802    struct ast_channel *chan)
01803 {
01804    int mute;
01805 
01806    /* Toggle user level mute request. */
01807    mute = !conference_bridge_user->muted;
01808    conference_bridge_user->muted = mute;
01809 
01810    conf_update_user_mute(conference_bridge_user);
01811    ast_test_suite_event_notify("CONF_MUTE",
01812       "Message: participant %s %s\r\n"
01813       "Conference: %s\r\n"
01814       "Channel: %s",
01815       ast_channel_name(chan),
01816       mute ? "muted" : "unmuted",
01817       conference_bridge_user->b_profile.name,
01818       ast_channel_name(chan));
01819 
01820    return ast_stream_and_wait(chan, (mute ?
01821       conf_get_sound(CONF_SOUND_MUTED, conference_bridge_user->b_profile.sounds) :
01822       conf_get_sound(CONF_SOUND_UNMUTED, conference_bridge_user->b_profile.sounds)),
01823       "");
01824 }
01825 
01826 static int action_toggle_mute_participants(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
01827 {
01828    struct conference_bridge_user *participant = NULL;
01829    const char *sound_to_play;
01830    int mute;
01831 
01832    ao2_lock(conference_bridge);
01833 
01834    /* Toggle bridge level mute request. */
01835    mute = !conference_bridge->muted;
01836    conference_bridge->muted = mute;
01837 
01838    AST_LIST_TRAVERSE(&conference_bridge->active_list, participant, list) {
01839       if (!ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
01840          /* Set user level to bridge level mute request. */
01841          participant->muted = mute;
01842          conf_update_user_mute(participant);
01843       }
01844    }
01845 
01846    ao2_unlock(conference_bridge);
01847 
01848    sound_to_play = conf_get_sound((mute ? CONF_SOUND_PARTICIPANTS_MUTED : CONF_SOUND_PARTICIPANTS_UNMUTED),
01849       conference_bridge_user->b_profile.sounds);
01850 
01851    /* The host needs to hear it seperately, as they don't get the audio from play_sound_helper */
01852    ast_stream_and_wait(conference_bridge_user->chan, sound_to_play, "");
01853 
01854    /* Announce to the group that all participants are muted */
01855    ast_autoservice_start(conference_bridge_user->chan);
01856    play_sound_helper(conference_bridge, sound_to_play, 0);
01857    ast_autoservice_stop(conference_bridge_user->chan);
01858 
01859    return 0;
01860 }
01861 
01862 static int action_playback(struct ast_bridge_channel *bridge_channel, const char *playback_file)
01863 {
01864    char *file_copy = ast_strdupa(playback_file);
01865    char *file = NULL;
01866 
01867    while ((file = strsep(&file_copy, "&"))) {
01868       if (ast_stream_and_wait(bridge_channel->chan, file, "")) {
01869          ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
01870          return -1;
01871       }
01872    }
01873    return 0;
01874 }
01875 
01876 static int action_playback_and_continue(struct conference_bridge *conference_bridge,
01877    struct conference_bridge_user *conference_bridge_user,
01878    struct ast_bridge_channel *bridge_channel,
01879    struct conf_menu *menu,
01880    const char *playback_file,
01881    const char *cur_dtmf,
01882    int *stop_prompts)
01883 {
01884    int i;
01885    int digit = 0;
01886    char dtmf[MAXIMUM_DTMF_FEATURE_STRING];
01887    struct conf_menu_entry new_menu_entry = { { 0, }, };
01888    char *file_copy = ast_strdupa(playback_file);
01889    char *file = NULL;
01890 
01891    while ((file = strsep(&file_copy, "&"))) {
01892       if (ast_streamfile(bridge_channel->chan, file, ast_channel_language(bridge_channel->chan))) {
01893          ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
01894          return -1;
01895       }
01896 
01897       /* now wait for more digits. */
01898       if (!(digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY))) {
01899          /* streaming finished and no DTMF was entered */
01900          continue;
01901       } else if (digit == -1) {
01902          /* error */
01903          return -1;
01904       } else {
01905          break; /* dtmf was entered */
01906       }
01907    }
01908    if (!digit) {
01909       /* streaming finished on all files and no DTMF was entered */
01910       return -1;
01911    }
01912    ast_stopstream(bridge_channel->chan);
01913 
01914    /* If we get here, then DTMF has been entered, This means no
01915     * additional prompts should be played for this menu entry */
01916    *stop_prompts = 1;
01917 
01918    /* If a digit was pressed during the payback, update
01919     * the dtmf string and look for a new menu entry in the
01920     * menu structure */
01921    ast_copy_string(dtmf, cur_dtmf, sizeof(dtmf));
01922    for (i = 0; i < (MAXIMUM_DTMF_FEATURE_STRING - 1); i++) {
01923       dtmf[i] = cur_dtmf[i];
01924       if (!dtmf[i]) {
01925          dtmf[i] = (char) digit;
01926          dtmf[i + 1] = '\0';
01927          i = -1;
01928          break;
01929       }
01930    }
01931    /* If i is not -1 then the new dtmf digit was _NOT_ added to the string.
01932     * If this is the case, no new DTMF sequence should be looked for. */
01933    if (i != -1) {
01934       return 0;
01935    }
01936 
01937    if (conf_find_menu_entry_by_sequence(dtmf, menu, &new_menu_entry)) {
01938       execute_menu_entry(conference_bridge,
01939          conference_bridge_user,
01940          bridge_channel,
01941          &new_menu_entry, menu);
01942       conf_menu_entry_destroy(&new_menu_entry);
01943    }
01944    return 0;
01945 }
01946 
01947 static int action_kick_last(struct conference_bridge *conference_bridge,
01948    struct ast_bridge_channel *bridge_channel,
01949    struct conference_bridge_user *conference_bridge_user)
01950 {
01951    struct conference_bridge_user *last_participant = NULL;
01952    int isadmin = ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN);
01953 
01954    if (!isadmin) {
01955       ast_stream_and_wait(bridge_channel->chan,
01956          conf_get_sound(CONF_SOUND_ERROR_MENU, conference_bridge_user->b_profile.sounds),
01957          "");
01958       ast_log(LOG_WARNING, "Only admin users can use the kick_last menu action. Channel %s of conf %s is not an admin.\n",
01959          ast_channel_name(bridge_channel->chan),
01960          conference_bridge->name);
01961       return -1;
01962    }
01963 
01964    ao2_lock(conference_bridge);
01965    if (((last_participant = AST_LIST_LAST(&conference_bridge->active_list)) == conference_bridge_user)
01966       || (ast_test_flag(&last_participant->u_profile, USER_OPT_ADMIN))) {
01967       ao2_unlock(conference_bridge);
01968       ast_stream_and_wait(bridge_channel->chan,
01969          conf_get_sound(CONF_SOUND_ERROR_MENU, conference_bridge_user->b_profile.sounds),
01970          "");
01971    } else if (last_participant && !last_participant->kicked) {
01972       last_participant->kicked = 1;
01973       ast_bridge_remove(conference_bridge->bridge, last_participant->chan);
01974       ao2_unlock(conference_bridge);
01975    }
01976    return 0;
01977 }
01978 
01979 static int action_dialplan_exec(struct ast_bridge_channel *bridge_channel, struct conf_menu_action *menu_action)
01980 {
01981    struct ast_pbx_args args;
01982    struct ast_pbx *pbx;
01983    char *exten;
01984    char *context;
01985    int priority;
01986    int res;
01987 
01988    memset(&args, 0, sizeof(args));
01989    args.no_hangup_chan = 1;
01990 
01991    ast_channel_lock(bridge_channel->chan);
01992 
01993    /*save off*/
01994    exten = ast_strdupa(ast_channel_exten(bridge_channel->chan));
01995    context = ast_strdupa(ast_channel_context(bridge_channel->chan));
01996    priority = ast_channel_priority(bridge_channel->chan);
01997    pbx = ast_channel_pbx(bridge_channel->chan);
01998    ast_channel_pbx_set(bridge_channel->chan, NULL);
01999 
02000    /*set new*/
02001    ast_channel_exten_set(bridge_channel->chan, menu_action->data.dialplan_args.exten);
02002    ast_channel_context_set(bridge_channel->chan, menu_action->data.dialplan_args.context);
02003    ast_channel_priority_set(bridge_channel->chan, menu_action->data.dialplan_args.priority);
02004 
02005    ast_channel_unlock(bridge_channel->chan);
02006 
02007    /*execute*/
02008    res = ast_pbx_run_args(bridge_channel->chan, &args);
02009 
02010    /*restore*/
02011    ast_channel_lock(bridge_channel->chan);
02012 
02013    ast_channel_exten_set(bridge_channel->chan, exten);
02014    ast_channel_context_set(bridge_channel->chan, context);
02015    ast_channel_priority_set(bridge_channel->chan, priority);
02016    ast_channel_pbx_set(bridge_channel->chan, pbx);
02017 
02018    ast_channel_unlock(bridge_channel->chan);
02019 
02020    return res;
02021 }
02022 
02023 static int execute_menu_entry(struct conference_bridge *conference_bridge,
02024    struct conference_bridge_user *conference_bridge_user,
02025    struct ast_bridge_channel *bridge_channel,
02026    struct conf_menu_entry *menu_entry,
02027    struct conf_menu *menu)
02028 {
02029    struct conf_menu_action *menu_action;
02030    int isadmin = ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN);
02031    int stop_prompts = 0;
02032    int res = 0;
02033 
02034    AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
02035       switch (menu_action->id) {
02036       case MENU_ACTION_TOGGLE_MUTE:
02037          res |= action_toggle_mute(conference_bridge,
02038             conference_bridge_user,
02039             bridge_channel->chan);
02040          break;
02041       case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
02042          if (!isadmin) {
02043             break;
02044          }
02045          action_toggle_mute_participants(conference_bridge, conference_bridge_user);
02046          break;
02047       case MENU_ACTION_PARTICIPANT_COUNT:
02048          announce_user_count(conference_bridge, conference_bridge_user);
02049          break;
02050       case MENU_ACTION_PLAYBACK:
02051          if (!stop_prompts) {
02052             res |= action_playback(bridge_channel, menu_action->data.playback_file);
02053          }
02054          break;
02055       case MENU_ACTION_RESET_LISTENING:
02056          ast_audiohook_volume_set(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 0);
02057          break;
02058       case MENU_ACTION_RESET_TALKING:
02059          ast_audiohook_volume_set(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 0);
02060          break;
02061       case MENU_ACTION_INCREASE_LISTENING:
02062          ast_audiohook_volume_adjust(conference_bridge_user->chan,
02063             AST_AUDIOHOOK_DIRECTION_WRITE, 1);
02064          break;
02065       case MENU_ACTION_DECREASE_LISTENING:
02066          ast_audiohook_volume_adjust(conference_bridge_user->chan,
02067             AST_AUDIOHOOK_DIRECTION_WRITE, -1);
02068          break;
02069       case MENU_ACTION_INCREASE_TALKING:
02070          ast_audiohook_volume_adjust(conference_bridge_user->chan,
02071             AST_AUDIOHOOK_DIRECTION_READ, 1);
02072          break;
02073       case MENU_ACTION_DECREASE_TALKING:
02074          ast_audiohook_volume_adjust(conference_bridge_user->chan,
02075             AST_AUDIOHOOK_DIRECTION_READ, -1);
02076          break;
02077       case MENU_ACTION_PLAYBACK_AND_CONTINUE:
02078          if (!(stop_prompts)) {
02079             res |= action_playback_and_continue(conference_bridge,
02080                conference_bridge_user,
02081                bridge_channel,
02082                menu,
02083                menu_action->data.playback_file,
02084                menu_entry->dtmf,
02085                &stop_prompts);
02086          }
02087          break;
02088       case MENU_ACTION_DIALPLAN_EXEC:
02089          res |= action_dialplan_exec(bridge_channel, menu_action);
02090          break;
02091       case MENU_ACTION_ADMIN_TOGGLE_LOCK:
02092          if (!isadmin) {
02093             break;
02094          }
02095          conference_bridge->locked = (!conference_bridge->locked ? 1 : 0);
02096          res |= ast_stream_and_wait(bridge_channel->chan,
02097             (conference_bridge->locked ?
02098             conf_get_sound(CONF_SOUND_LOCKED_NOW, conference_bridge_user->b_profile.sounds) :
02099             conf_get_sound(CONF_SOUND_UNLOCKED_NOW, conference_bridge_user->b_profile.sounds)),
02100             "");
02101 
02102          break;
02103       case MENU_ACTION_ADMIN_KICK_LAST:
02104          res |= action_kick_last(conference_bridge, bridge_channel, conference_bridge_user);
02105          break;
02106       case MENU_ACTION_LEAVE:
02107          ao2_lock(conference_bridge);
02108          ast_bridge_remove(conference_bridge->bridge, bridge_channel->chan);
02109          ao2_unlock(conference_bridge);
02110          break;
02111       case MENU_ACTION_NOOP:
02112          break;
02113       case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
02114          ao2_lock(conference_bridge);
02115          ast_bridge_set_single_src_video_mode(conference_bridge->bridge, bridge_channel->chan);
02116          ao2_unlock(conference_bridge);
02117          break;
02118       case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
02119          handle_video_on_exit(conference_bridge, bridge_channel->chan);
02120          break;
02121       }
02122    }
02123    return res;
02124 }
02125 
02126 int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel,
02127    struct conference_bridge_user *conference_bridge_user,
02128    struct conf_menu_entry *menu_entry,
02129    struct conf_menu *menu)
02130 {
02131    /* See if music on hold is playing */
02132    conf_moh_suspend(conference_bridge_user);
02133 
02134    /* execute the list of actions associated with this menu entry */
02135    execute_menu_entry(conference_bridge_user->conference_bridge, conference_bridge_user, bridge_channel, menu_entry, menu);
02136 
02137    /* See if music on hold needs to be started back up again */
02138    conf_moh_unsuspend(conference_bridge_user);
02139 
02140    return 0;
02141 }
02142 
02143 static int kick_conference_participant(struct conference_bridge *bridge, const char *channel)
02144 {
02145    struct conference_bridge_user *participant = NULL;
02146 
02147    ao2_lock(bridge);
02148    AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
02149       if (!strcasecmp(ast_channel_name(participant->chan), channel) && !participant->kicked) {
02150          participant->kicked = 1;
02151          ast_bridge_remove(bridge->bridge, participant->chan);
02152          ao2_unlock(bridge);
02153          return 0;
02154       }
02155    }
02156    AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) {
02157       if (!strcasecmp(ast_channel_name(participant->chan), channel) && !participant->kicked) {
02158          participant->kicked = 1;
02159          ast_bridge_remove(bridge->bridge, participant->chan);
02160          ao2_unlock(bridge);
02161          return 0;
02162       }
02163    }
02164    ao2_unlock(bridge);
02165 
02166    return -1;
02167 }
02168 
02169 static char *complete_confbridge_name(const char *line, const char *word, int pos, int state)
02170 {
02171    int which = 0;
02172    struct conference_bridge *bridge = NULL;
02173    char *res = NULL;
02174    int wordlen = strlen(word);
02175    struct ao2_iterator i;
02176 
02177    i = ao2_iterator_init(conference_bridges, 0);
02178    while ((bridge = ao2_iterator_next(&i))) {
02179       if (!strncasecmp(bridge->name, word, wordlen) && ++which > state) {
02180          res = ast_strdup(bridge->name);
02181          ao2_ref(bridge, -1);
02182          break;
02183       }
02184       ao2_ref(bridge, -1);
02185    }
02186    ao2_iterator_destroy(&i);
02187 
02188    return res;
02189 }
02190 
02191 static char *complete_confbridge_participant(const char *bridge_name, const char *line, const char *word, int pos, int state)
02192 {
02193    int which = 0;
02194    RAII_VAR(struct conference_bridge *, bridge, NULL, ao2_cleanup);
02195    struct conference_bridge tmp;
02196    struct conference_bridge_user *participant;
02197    char *res = NULL;
02198    int wordlen = strlen(word);
02199 
02200    ast_copy_string(tmp.name, bridge_name, sizeof(tmp.name));
02201    bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02202    if (!bridge) {
02203       return NULL;
02204    }
02205 
02206    ao2_lock(bridge);
02207    AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
02208       if (!strncasecmp(ast_channel_name(participant->chan), word, wordlen) && ++which > state) {
02209          res = ast_strdup(ast_channel_name(participant->chan));
02210          ao2_unlock(bridge);
02211          return res;
02212       }
02213    }
02214 
02215    AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) {
02216       if (!strncasecmp(ast_channel_name(participant->chan), word, wordlen) && ++which > state) {
02217          res = ast_strdup(ast_channel_name(participant->chan));
02218          ao2_unlock(bridge);
02219          return res;
02220       }
02221    }
02222    ao2_unlock(bridge);
02223 
02224    return NULL;
02225 }
02226 
02227 static char *handle_cli_confbridge_kick(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02228 {
02229    struct conference_bridge *bridge = NULL;
02230    struct conference_bridge tmp;
02231 
02232    switch (cmd) {
02233    case CLI_INIT:
02234       e->command = "confbridge kick";
02235       e->usage =
02236          "Usage: confbridge kick <conference> <channel>\n"
02237          "       Kicks a channel out of the conference bridge.\n";
02238       return NULL;
02239    case CLI_GENERATE:
02240       if (a->pos == 2) {
02241          return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02242       }
02243       if (a->pos == 3) {
02244          return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
02245       }
02246       return NULL;
02247    }
02248 
02249    if (a->argc != 4) {
02250       return CLI_SHOWUSAGE;
02251    }
02252 
02253    ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
02254    bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02255    if (!bridge) {
02256       ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
02257       return CLI_SUCCESS;
02258    }
02259    if (kick_conference_participant(bridge, a->argv[3])) {
02260       ast_cli(a->fd, "No participant named '%s' found!\n", a->argv[3]);
02261       return CLI_SUCCESS;
02262    }
02263    ao2_ref(bridge, -1);
02264    ast_cli(a->fd, "Participant '%s' kicked out of conference '%s'\n", a->argv[3], a->argv[2]);
02265    return CLI_SUCCESS;
02266 }
02267 
02268 static void handle_cli_confbridge_list_item(struct ast_cli_args *a, struct conference_bridge_user *participant)
02269 {
02270    ast_cli(a->fd, "%-30s %-16s %-16s %-16s %-16s %s\n",
02271       ast_channel_name(participant->chan),
02272       participant->u_profile.name,
02273       participant->b_profile.name,
02274       participant->menu_name,
02275       S_COR(ast_channel_caller(participant->chan)->id.number.valid,
02276          ast_channel_caller(participant->chan)->id.number.str, "<unknown>"),
02277       AST_CLI_YESNO(participant->muted));
02278 }
02279 
02280 static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02281 {
02282    struct ao2_iterator i;
02283    struct conference_bridge *bridge = NULL;
02284    struct conference_bridge tmp;
02285    struct conference_bridge_user *participant = NULL;
02286 
02287    switch (cmd) {
02288    case CLI_INIT:
02289       e->command = "confbridge list";
02290       e->usage =
02291          "Usage: confbridge list [<name>]\n"
02292          "       Lists all currently active conference bridges.\n";
02293       return NULL;
02294    case CLI_GENERATE:
02295       if (a->pos == 2) {
02296          return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02297       }
02298       return NULL;
02299    }
02300 
02301    if (a->argc == 2) {
02302       ast_cli(a->fd, "Conference Bridge Name           Users  Marked Locked?\n");
02303       ast_cli(a->fd, "================================ ====== ====== ========\n");
02304       i = ao2_iterator_init(conference_bridges, 0);
02305       while ((bridge = ao2_iterator_next(&i))) {
02306          ast_cli(a->fd, "%-32s %6i %6i %s\n", bridge->name, bridge->activeusers + bridge->waitingusers, bridge->markedusers, (bridge->locked ? "locked" : "unlocked"));
02307          ao2_ref(bridge, -1);
02308       }
02309       ao2_iterator_destroy(&i);
02310       return CLI_SUCCESS;
02311    }
02312 
02313    if (a->argc == 3) {
02314       ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
02315       bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02316       if (!bridge) {
02317          ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
02318          return CLI_SUCCESS;
02319       }
02320       ast_cli(a->fd, "Channel                        User Profile     Bridge Profile   Menu             CallerID         Muted\n");
02321       ast_cli(a->fd, "============================== ================ ================ ================ ================ =====\n");
02322       ao2_lock(bridge);
02323       AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
02324          handle_cli_confbridge_list_item(a, participant);
02325       }
02326       AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) {
02327          handle_cli_confbridge_list_item(a, participant);
02328       }
02329       ao2_unlock(bridge);
02330       ao2_ref(bridge, -1);
02331       return CLI_SUCCESS;
02332    }
02333 
02334    return CLI_SHOWUSAGE;
02335 }
02336 
02337 /* \internal
02338  * \brief finds a conference by name and locks/unlocks.
02339  *
02340  * \retval 0 success
02341  * \retval -1 conference not found
02342  */
02343 static int generic_lock_unlock_helper(int lock, const char *conference)
02344 {
02345    struct conference_bridge *bridge = NULL;
02346    struct conference_bridge tmp;
02347    int res = 0;
02348 
02349    ast_copy_string(tmp.name, conference, sizeof(tmp.name));
02350    bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02351    if (!bridge) {
02352       return -1;
02353    }
02354    ao2_lock(bridge);
02355    bridge->locked = lock;
02356    ast_test_suite_event_notify("CONF_LOCK", "Message: conference %s\r\nConference: %s", bridge->locked ? "locked" : "unlocked", bridge->b_profile.name);
02357    ao2_unlock(bridge);
02358    ao2_ref(bridge, -1);
02359 
02360    return res;
02361 }
02362 
02363 /* \internal
02364  * \brief finds a conference user by channel name and mutes/unmutes them.
02365  *
02366  * \retval 0 success
02367  * \retval -1 conference not found
02368  * \retval -2 user not found
02369  */
02370 static int generic_mute_unmute_helper(int mute, const char *conference, const char *user)
02371 {
02372    struct conference_bridge *bridge = NULL;
02373    struct conference_bridge tmp;
02374    struct conference_bridge_user *participant = NULL;
02375    int res = 0;
02376    ast_copy_string(tmp.name, conference, sizeof(tmp.name));
02377    bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02378    if (!bridge) {
02379       return -1;
02380    }
02381    ao2_lock(bridge);
02382    AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
02383       if (!strncmp(user, ast_channel_name(participant->chan), strlen(user))) {
02384          break;
02385       }
02386    }
02387    if (participant) {
02388       /* Set user level mute request. */
02389       participant->muted = mute ? 1 : 0;
02390 
02391       conf_update_user_mute(participant);
02392       ast_test_suite_event_notify("CONF_MUTE",
02393          "Message: participant %s %s\r\n"
02394          "Conference: %s\r\n"
02395          "Channel: %s",
02396          ast_channel_name(participant->chan),
02397          mute ? "muted" : "unmuted",
02398          bridge->b_profile.name,
02399          ast_channel_name(participant->chan));
02400    } else {
02401       res = -2;;
02402    }
02403    ao2_unlock(bridge);
02404    ao2_ref(bridge, -1);
02405 
02406    return res;
02407 }
02408 
02409 static int cli_mute_unmute_helper(int mute, struct ast_cli_args *a)
02410 {
02411    int res = generic_mute_unmute_helper(mute, a->argv[2], a->argv[3]);
02412 
02413    if (res == -1) {
02414       ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
02415       return -1;
02416    } else if (res == -2) {
02417       ast_cli(a->fd, "No channel named '%s' found in conference %s\n", a->argv[3], a->argv[2]);
02418       return -1;
02419    }
02420    ast_cli(a->fd, "%s %s from confbridge %s\n", mute ? "Muting" : "Unmuting", a->argv[3], a->argv[2]);
02421    return 0;
02422 }
02423 
02424 static char *handle_cli_confbridge_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02425 {
02426    switch (cmd) {
02427    case CLI_INIT:
02428       e->command = "confbridge mute";
02429       e->usage =
02430          "Usage: confbridge mute <conference> <channel>\n"
02431          "       Mute a channel in a conference.\n"
02432          "       If the specified channel is a prefix,\n"
02433          "       the action will be taken on the first\n"
02434          "       matching channel.\n";
02435       return NULL;
02436    case CLI_GENERATE:
02437       if (a->pos == 2) {
02438          return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02439       }
02440       if (a->pos == 3) {
02441          return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
02442       }
02443       return NULL;
02444    }
02445    if (a->argc != 4) {
02446       return CLI_SHOWUSAGE;
02447    }
02448 
02449    cli_mute_unmute_helper(1, a);
02450 
02451    return CLI_SUCCESS;
02452 }
02453 
02454 static char *handle_cli_confbridge_unmute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02455 {
02456    switch (cmd) {
02457    case CLI_INIT:
02458       e->command = "confbridge unmute";
02459       e->usage =
02460          "Usage: confbridge unmute <conference> <channel>\n"
02461          "       Unmute a channel in a conference.\n"
02462          "       If the specified channel is a prefix,\n"
02463          "       the action will be taken on the first\n"
02464          "       matching channel.\n";
02465       return NULL;
02466    case CLI_GENERATE:
02467       if (a->pos == 2) {
02468          return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02469       }
02470       if (a->pos == 3) {
02471          return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
02472       }
02473       return NULL;
02474    }
02475    if (a->argc != 4) {
02476       return CLI_SHOWUSAGE;
02477    }
02478 
02479    cli_mute_unmute_helper(0, a);
02480 
02481    return CLI_SUCCESS;
02482 }
02483 
02484 static char *handle_cli_confbridge_lock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02485 {
02486    switch (cmd) {
02487    case CLI_INIT:
02488       e->command = "confbridge lock";
02489       e->usage =
02490          "Usage: confbridge lock <conference>\n"
02491          "       Lock a conference. While locked, no new non-admins\n"
02492          "       may join the conference.\n";
02493       return NULL;
02494    case CLI_GENERATE:
02495       if (a->pos == 2) {
02496          return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02497       }
02498       return NULL;
02499    }
02500    if (a->argc != 3) {
02501       return CLI_SHOWUSAGE;
02502    }
02503    if (generic_lock_unlock_helper(1, a->argv[2])) {
02504       ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
02505    } else {
02506       ast_cli(a->fd, "Conference %s is locked.\n", a->argv[2]);
02507    }
02508    return CLI_SUCCESS;
02509 }
02510 
02511 static char *handle_cli_confbridge_unlock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02512 {
02513    switch (cmd) {
02514    case CLI_INIT:
02515       e->command = "confbridge unlock";
02516       e->usage =
02517          "Usage: confbridge unlock <conference>\n"
02518          "       Unlock a previously locked conference.\n";
02519       return NULL;
02520    case CLI_GENERATE:
02521       if (a->pos == 2) {
02522          return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02523       }
02524       return NULL;
02525    }
02526    if (a->argc != 3) {
02527       return CLI_SHOWUSAGE;
02528    }
02529    if (generic_lock_unlock_helper(0, a->argv[2])) {
02530       ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
02531    } else {
02532       ast_cli(a->fd, "Conference %s is unlocked.\n", a->argv[2]);
02533    }
02534    return CLI_SUCCESS;
02535 }
02536 
02537 static char *handle_cli_confbridge_start_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02538 {
02539    const char *rec_file = NULL;
02540    struct conference_bridge *bridge = NULL;
02541    struct conference_bridge tmp;
02542 
02543    switch (cmd) {
02544    case CLI_INIT:
02545       e->command = "confbridge record start";
02546       e->usage =
02547          "Usage: confbridge record start <conference> <file>\n"
02548          "       <file> is optional, Otherwise the bridge profile\n"
02549          "       record file will be used.  If the bridge profile\n"
02550          "       has no record file specified, a file will automatically\n"
02551          "       be generated in the monitor directory\n";
02552       return NULL;
02553    case CLI_GENERATE:
02554       if (a->pos == 3) {
02555          return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02556       }
02557       return NULL;
02558    }
02559    if (a->argc < 4) {
02560       return CLI_SHOWUSAGE;
02561    }
02562    if (a->argc == 5) {
02563       rec_file = a->argv[4];
02564    }
02565 
02566    ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
02567    bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02568    if (!bridge) {
02569       ast_cli(a->fd, "Conference not found.\n");
02570       return CLI_FAILURE;
02571    }
02572    ao2_lock(bridge);
02573    if (conf_is_recording(bridge)) {
02574       ast_cli(a->fd, "Conference is already being recorded.\n");
02575       ao2_unlock(bridge);
02576       ao2_ref(bridge, -1);
02577       return CLI_SUCCESS;
02578    }
02579    if (!ast_strlen_zero(rec_file)) {
02580       ast_copy_string(bridge->b_profile.rec_file, rec_file, sizeof(bridge->b_profile.rec_file));
02581    }
02582 
02583    if (start_conf_record_thread(bridge)) {
02584       ast_cli(a->fd, "Could not start recording due to internal error.\n");
02585       ao2_unlock(bridge);
02586       ao2_ref(bridge, -1);
02587       return CLI_FAILURE;
02588    }
02589    ao2_unlock(bridge);
02590 
02591    ast_cli(a->fd, "Recording started\n");
02592    ao2_ref(bridge, -1);
02593    return CLI_SUCCESS;
02594 }
02595 
02596 static char *handle_cli_confbridge_stop_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02597 {
02598    struct conference_bridge *bridge = NULL;
02599    struct conference_bridge tmp;
02600    int ret;
02601 
02602    switch (cmd) {
02603    case CLI_INIT:
02604       e->command = "confbridge record stop";
02605       e->usage =
02606          "Usage: confbridge record stop <conference>\n"
02607          "       Stop a previously started recording.\n";
02608       return NULL;
02609    case CLI_GENERATE:
02610       if (a->pos == 3) {
02611          return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02612       }
02613       return NULL;
02614    }
02615    if (a->argc != 4) {
02616       return CLI_SHOWUSAGE;
02617    }
02618 
02619    ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
02620    bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02621    if (!bridge) {
02622       ast_cli(a->fd, "Conference not found.\n");
02623       return CLI_SUCCESS;
02624    }
02625    ao2_lock(bridge);
02626    ret = conf_stop_record(bridge);
02627    ao2_unlock(bridge);
02628    ast_cli(a->fd, "Recording %sstopped.\n", ret ? "could not be " : "");
02629    ao2_ref(bridge, -1);
02630    return CLI_SUCCESS;
02631 }
02632 
02633 static struct ast_cli_entry cli_confbridge[] = {
02634    AST_CLI_DEFINE(handle_cli_confbridge_list, "List conference bridges and participants."),
02635    AST_CLI_DEFINE(handle_cli_confbridge_kick, "Kick participants out of conference bridges."),
02636    AST_CLI_DEFINE(handle_cli_confbridge_mute, "Mute a participant."),
02637    AST_CLI_DEFINE(handle_cli_confbridge_unmute, "Unmute a participant."),
02638    AST_CLI_DEFINE(handle_cli_confbridge_lock, "Lock a conference."),
02639    AST_CLI_DEFINE(handle_cli_confbridge_unlock, "Unlock a conference."),
02640    AST_CLI_DEFINE(handle_cli_confbridge_start_record, "Start recording a conference"),
02641    AST_CLI_DEFINE(handle_cli_confbridge_stop_record, "Stop recording a conference."),
02642 };
02643 static struct ast_custom_function confbridge_function = {
02644    .name = "CONFBRIDGE",
02645    .write = func_confbridge_helper,
02646 };
02647 
02648 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
02649 static struct ast_custom_function confbridge_info_function = {
02650    .name = "CONFBRIDGE_INFO",
02651    .read = func_confbridge_info,
02652 };
02653 
02654 static void action_confbridgelist_item(struct mansession *s, const char *id_text, struct conference_bridge *bridge, struct conference_bridge_user *participant)
02655 {
02656    astman_append(s,
02657       "Event: ConfbridgeList\r\n"
02658       "%s"
02659       "Conference: %s\r\n"
02660       "CallerIDNum: %s\r\n"
02661       "CallerIDName: %s\r\n"
02662       "Channel: %s\r\n"
02663       "Admin: %s\r\n"
02664       "MarkedUser: %s\r\n"
02665       "Muted: %s\r\n"
02666       "\r\n",
02667       id_text,
02668       bridge->name,
02669       S_COR(ast_channel_caller(participant->chan)->id.number.valid, ast_channel_caller(participant->chan)->id.number.str, "<unknown>"),
02670       S_COR(ast_channel_caller(participant->chan)->id.name.valid, ast_channel_caller(participant->chan)->id.name.str, "<no name>"),
02671       ast_channel_name(participant->chan),
02672       ast_test_flag(&participant->u_profile, USER_OPT_ADMIN) ? "Yes" : "No",
02673       ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER) ? "Yes" : "No",
02674       participant->muted ? "Yes" : "No");
02675 }
02676 
02677 static int action_confbridgelist(struct mansession *s, const struct message *m)
02678 {
02679    const char *actionid = astman_get_header(m, "ActionID");
02680    const char *conference = astman_get_header(m, "Conference");
02681    struct conference_bridge_user *participant = NULL;
02682    struct conference_bridge *bridge = NULL;
02683    struct conference_bridge tmp;
02684    char id_text[80] = "";
02685    int total = 0;
02686 
02687    if (!ast_strlen_zero(actionid)) {
02688       snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
02689    }
02690    if (ast_strlen_zero(conference)) {
02691       astman_send_error(s, m, "No Conference name provided.");
02692       return 0;
02693    }
02694    if (!ao2_container_count(conference_bridges)) {
02695       astman_send_error(s, m, "No active conferences.");
02696       return 0;
02697    }
02698    ast_copy_string(tmp.name, conference, sizeof(tmp.name));
02699    bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02700    if (!bridge) {
02701       astman_send_error(s, m, "No Conference by that name found.");
02702       return 0;
02703    }
02704 
02705    astman_send_listack(s, m, "Confbridge user list will follow", "start");
02706 
02707    ao2_lock(bridge);
02708    AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
02709       total++;
02710       action_confbridgelist_item(s, id_text, bridge, participant);
02711    }
02712    AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) {
02713       total++;
02714       action_confbridgelist_item(s, id_text, bridge, participant);
02715    }
02716    ao2_unlock(bridge);
02717    ao2_ref(bridge, -1);
02718 
02719    astman_append(s,
02720    "Event: ConfbridgeListComplete\r\n"
02721    "EventList: Complete\r\n"
02722    "ListItems: %d\r\n"
02723    "%s"
02724    "\r\n", total, id_text);
02725 
02726    return 0;
02727 }
02728 
02729 static int action_confbridgelistrooms(struct mansession *s, const struct message *m)
02730 {
02731    const char *actionid = astman_get_header(m, "ActionID");
02732    struct conference_bridge *bridge = NULL;
02733    struct ao2_iterator i;
02734    char id_text[512] = "";
02735    int totalitems = 0;
02736 
02737    if (!ast_strlen_zero(actionid)) {
02738       snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
02739    }
02740 
02741    if (!ao2_container_count(conference_bridges)) {
02742       astman_send_error(s, m, "No active conferences.");
02743       return 0;
02744    }
02745 
02746    astman_send_listack(s, m, "Confbridge conferences will follow", "start");
02747 
02748    /* Traverse the conference list */
02749    i = ao2_iterator_init(conference_bridges, 0);
02750    while ((bridge = ao2_iterator_next(&i))) {
02751       totalitems++;
02752 
02753       ao2_lock(bridge);
02754       astman_append(s,
02755       "Event: ConfbridgeListRooms\r\n"
02756       "%s"
02757       "Conference: %s\r\n"
02758       "Parties: %d\r\n"
02759       "Marked: %d\r\n"
02760       "Locked: %s\r\n"
02761       "\r\n",
02762       id_text,
02763       bridge->name,
02764       bridge->activeusers + bridge->waitingusers,
02765       bridge->markedusers,
02766       bridge->locked ? "Yes" : "No"); 
02767       ao2_unlock(bridge);
02768 
02769       ao2_ref(bridge, -1);
02770    }
02771    ao2_iterator_destroy(&i);
02772 
02773    /* Send final confirmation */
02774    astman_append(s,
02775    "Event: ConfbridgeListRoomsComplete\r\n"
02776    "EventList: Complete\r\n"
02777    "ListItems: %d\r\n"
02778    "%s"
02779    "\r\n", totalitems, id_text);
02780    return 0;
02781 }
02782 
02783 static int action_mute_unmute_helper(struct mansession *s, const struct message *m, int mute)
02784 {
02785    const char *conference = astman_get_header(m, "Conference");
02786    const char *channel = astman_get_header(m, "Channel");
02787    int res = 0;
02788 
02789    if (ast_strlen_zero(conference)) {
02790       astman_send_error(s, m, "No Conference name provided.");
02791       return 0;
02792    }
02793    if (ast_strlen_zero(channel)) {
02794       astman_send_error(s, m, "No channel name provided.");
02795       return 0;
02796    }
02797    if (!ao2_container_count(conference_bridges)) {
02798       astman_send_error(s, m, "No active conferences.");
02799       return 0;
02800    }
02801 
02802    res = generic_mute_unmute_helper(mute, conference, channel);
02803 
02804    if (res == -1) {
02805       astman_send_error(s, m, "No Conference by that name found.");
02806       return 0;
02807    } else if (res == -2) {
02808       astman_send_error(s, m, "No Channel by that name found in Conference.");
02809       return 0;
02810    }
02811 
02812    astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
02813    return 0;
02814 }
02815 
02816 static int action_confbridgeunmute(struct mansession *s, const struct message *m)
02817 {
02818    return action_mute_unmute_helper(s, m, 0);
02819 }
02820 static int action_confbridgemute(struct mansession *s, const struct message *m)
02821 {
02822    return action_mute_unmute_helper(s, m, 1);
02823 }
02824 
02825 static int action_lock_unlock_helper(struct mansession *s, const struct message *m, int lock)
02826 {
02827    const char *conference = astman_get_header(m, "Conference");
02828    int res = 0;
02829 
02830    if (ast_strlen_zero(conference)) {
02831       astman_send_error(s, m, "No Conference name provided.");
02832       return 0;
02833    }
02834    if (!ao2_container_count(conference_bridges)) {
02835       astman_send_error(s, m, "No active conferences.");
02836       return 0;
02837    }
02838    if ((res = generic_lock_unlock_helper(lock, conference))) {
02839       astman_send_error(s, m, "No Conference by that name found.");
02840       return 0;
02841    }
02842    astman_send_ack(s, m, lock ? "Conference locked" : "Conference unlocked");
02843    return 0;
02844 }
02845 static int action_confbridgeunlock(struct mansession *s, const struct message *m)
02846 {
02847    return action_lock_unlock_helper(s, m, 0);
02848 }
02849 static int action_confbridgelock(struct mansession *s, const struct message *m)
02850 {
02851    return action_lock_unlock_helper(s, m, 1);
02852 }
02853 
02854 static int action_confbridgekick(struct mansession *s, const struct message *m)
02855 {
02856    const char *conference = astman_get_header(m, "Conference");
02857    const char *channel = astman_get_header(m, "Channel");
02858    struct conference_bridge *bridge = NULL;
02859    struct conference_bridge tmp;
02860    int found = 0;
02861 
02862    if (ast_strlen_zero(conference)) {
02863       astman_send_error(s, m, "No Conference name provided.");
02864       return 0;
02865    }
02866    if (!ao2_container_count(conference_bridges)) {
02867       astman_send_error(s, m, "No active conferences.");
02868       return 0;
02869    }
02870 
02871    ast_copy_string(tmp.name, conference, sizeof(tmp.name));
02872    bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02873    if (!bridge) {
02874       astman_send_error(s, m, "No Conference by that name found.");
02875       return 0;
02876    }
02877 
02878    found = !kick_conference_participant(bridge, channel);
02879    ao2_ref(bridge, -1);
02880 
02881    if (found) {
02882       astman_send_ack(s, m, "User kicked");
02883    } else {
02884       astman_send_error(s, m, "No Channel by that name found in Conference.");
02885    }
02886    return 0;
02887 }
02888 
02889 static int action_confbridgestartrecord(struct mansession *s, const struct message *m)
02890 {
02891    const char *conference = astman_get_header(m, "Conference");
02892    const char *recordfile = astman_get_header(m, "RecordFile");
02893    struct conference_bridge *bridge = NULL;
02894    struct conference_bridge tmp;
02895 
02896    if (ast_strlen_zero(conference)) {
02897       astman_send_error(s, m, "No Conference name provided.");
02898       return 0;
02899    }
02900    if (!ao2_container_count(conference_bridges)) {
02901       astman_send_error(s, m, "No active conferences.");
02902       return 0;
02903    }
02904 
02905    ast_copy_string(tmp.name, conference, sizeof(tmp.name));
02906    bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02907    if (!bridge) {
02908       astman_send_error(s, m, "No Conference by that name found.");
02909       return 0;
02910    }
02911 
02912    ao2_lock(bridge);
02913    if (conf_is_recording(bridge)) {
02914       astman_send_error(s, m, "Conference is already being recorded.");
02915       ao2_unlock(bridge);
02916       ao2_ref(bridge, -1);
02917       return 0;
02918    }
02919 
02920    if (!ast_strlen_zero(recordfile)) {
02921       ast_copy_string(bridge->b_profile.rec_file, recordfile, sizeof(bridge->b_profile.rec_file));
02922    }
02923 
02924    if (start_conf_record_thread(bridge)) {
02925       astman_send_error(s, m, "Internal error starting conference recording.");
02926       ao2_unlock(bridge);
02927       ao2_ref(bridge, -1);
02928       return 0;
02929    }
02930    ao2_unlock(bridge);
02931 
02932    ao2_ref(bridge, -1);
02933    astman_send_ack(s, m, "Conference Recording Started.");
02934    return 0;
02935 }
02936 static int action_confbridgestoprecord(struct mansession *s, const struct message *m)
02937 {
02938    const char *conference = astman_get_header(m, "Conference");
02939    struct conference_bridge *bridge = NULL;
02940    struct conference_bridge tmp;
02941 
02942    if (ast_strlen_zero(conference)) {
02943       astman_send_error(s, m, "No Conference name provided.");
02944       return 0;
02945    }
02946    if (!ao2_container_count(conference_bridges)) {
02947       astman_send_error(s, m, "No active conferences.");
02948       return 0;
02949    }
02950 
02951    ast_copy_string(tmp.name, conference, sizeof(tmp.name));
02952    bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02953    if (!bridge) {
02954       astman_send_error(s, m, "No Conference by that name found.");
02955       return 0;
02956    }
02957 
02958    ao2_lock(bridge);
02959    if (conf_stop_record(bridge)) {
02960       ao2_unlock(bridge);
02961       astman_send_error(s, m, "Internal error while stopping recording.");
02962       ao2_ref(bridge, -1);
02963       return 0;
02964    }
02965    ao2_unlock(bridge);
02966 
02967    ao2_ref(bridge, -1);
02968    astman_send_ack(s, m, "Conference Recording Stopped.");
02969    return 0;
02970 }
02971 
02972 static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m)
02973 {
02974    const char *conference = astman_get_header(m, "Conference");
02975    const char *channel = astman_get_header(m, "Channel");
02976    struct conference_bridge_user *participant = NULL;
02977    struct conference_bridge *bridge = NULL;
02978    struct conference_bridge tmp;
02979 
02980    if (ast_strlen_zero(conference)) {
02981       astman_send_error(s, m, "No Conference name provided.");
02982       return 0;
02983    }
02984    if (ast_strlen_zero(channel)) {
02985       astman_send_error(s, m, "No channel name provided.");
02986       return 0;
02987    }
02988    if (!ao2_container_count(conference_bridges)) {
02989       astman_send_error(s, m, "No active conferences.");
02990       return 0;
02991    }
02992 
02993    ast_copy_string(tmp.name, conference, sizeof(tmp.name));
02994    bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02995    if (!bridge) {
02996       astman_send_error(s, m, "No Conference by that name found.");
02997       return 0;
02998    }
02999 
03000    /* find channel and set as video src. */
03001    ao2_lock(bridge);
03002    AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
03003       if (!strncmp(channel, ast_channel_name(participant->chan), strlen(channel))) {
03004          ast_bridge_set_single_src_video_mode(bridge->bridge, participant->chan);
03005          break;
03006       }
03007    }
03008    ao2_unlock(bridge);
03009    ao2_ref(bridge, -1);
03010 
03011    /* do not access participant after bridge unlock.  We are just
03012     * using this check to see if it was found or not */
03013    if (!participant) {
03014       astman_send_error(s, m, "No channel by that name found in conference.");
03015       return 0;
03016    }
03017    astman_send_ack(s, m, "Conference single video source set.");
03018    return 0;
03019 }
03020 
03021 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
03022 {
03023    char *parse = NULL;
03024    struct conference_bridge *bridge = NULL;
03025    struct conference_bridge_user *participant = NULL;
03026    struct conference_bridge tmp;
03027    int count = 0;
03028    AST_DECLARE_APP_ARGS(args,
03029       AST_APP_ARG(type);
03030       AST_APP_ARG(confno);
03031    );
03032 
03033    /* parse all the required arguments and make sure they exist. */
03034    if (ast_strlen_zero(data)) {
03035       return -1;
03036    }
03037    parse = ast_strdupa(data);
03038    AST_STANDARD_APP_ARGS(args, parse);
03039    if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
03040       return -1;
03041    }
03042    if (!ao2_container_count(conference_bridges)) {
03043       snprintf(buf, len, "0");
03044       return 0;
03045    }
03046    ast_copy_string(tmp.name, args.confno, sizeof(tmp.name));
03047    bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
03048    if (!bridge) {
03049       snprintf(buf, len, "0");
03050       return 0;
03051    }
03052 
03053    /* get the correct count for the type requested */
03054    ao2_lock(bridge);
03055    if (!strncasecmp(args.type, "parties", 7)) {
03056       AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
03057          count++;
03058       }
03059       AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) {
03060          count++;
03061       }
03062    } else if (!strncasecmp(args.type, "admins", 6)) {
03063       AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
03064          if (ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
03065             count++;
03066          }
03067       }
03068    } else if (!strncasecmp(args.type, "marked", 6)) {
03069       AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
03070          if (ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER)) {
03071             count++;
03072          }
03073       }
03074    } else if (!strncasecmp(args.type, "locked", 6)) {
03075       count = bridge->locked;
03076    } else {
03077       ast_log(LOG_ERROR, "Invalid keyword '%s' passed to CONFBRIDGE_INFO.  Should be one of: "
03078          "parties, admins, marked, or locked.\n", args.type);
03079    }
03080    snprintf(buf, len, "%d", count);
03081    ao2_unlock(bridge);
03082    ao2_ref(bridge, -1);
03083    return 0;
03084 }
03085 
03086 void conf_add_user_active(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
03087 {
03088    AST_LIST_INSERT_TAIL(&conference_bridge->active_list, cbu, list);
03089    conference_bridge->activeusers++;
03090 }
03091 
03092 void conf_add_user_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
03093 {
03094    AST_LIST_INSERT_TAIL(&conference_bridge->active_list, cbu, list);
03095    conference_bridge->activeusers++;
03096    conference_bridge->markedusers++;
03097 }
03098 
03099 void conf_add_user_waiting(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
03100 {
03101    AST_LIST_INSERT_TAIL(&conference_bridge->waiting_list, cbu, list);
03102    conference_bridge->waitingusers++;
03103 }
03104 
03105 void conf_remove_user_active(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
03106 {
03107    AST_LIST_REMOVE(&conference_bridge->active_list, cbu, list);
03108    conference_bridge->activeusers--;
03109 }
03110 
03111 void conf_remove_user_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
03112 {
03113    AST_LIST_REMOVE(&conference_bridge->active_list, cbu, list);
03114    conference_bridge->activeusers--;
03115    conference_bridge->markedusers--;
03116 }
03117 
03118 void conf_mute_only_active(struct conference_bridge *conference_bridge)
03119 {
03120    struct conference_bridge_user *only_participant = AST_LIST_FIRST(&conference_bridge->active_list);
03121 
03122    /* Turn on MOH if the single participant is set up for it */
03123    if (ast_test_flag(&only_participant->u_profile, USER_OPT_MUSICONHOLD)) {
03124       conf_moh_start(only_participant);
03125    }
03126    conf_update_user_mute(only_participant);
03127 }
03128 
03129 void conf_remove_user_waiting(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
03130 {
03131    AST_LIST_REMOVE(&conference_bridge->waiting_list, cbu, list);
03132    conference_bridge->waitingusers--;
03133 }
03134 
03135 /*! \brief Called when module is being unloaded */
03136 static int unload_module(void)
03137 {
03138    int res = ast_unregister_application(app);
03139 
03140    ast_custom_function_unregister(&confbridge_function);
03141    ast_custom_function_unregister(&confbridge_info_function);
03142 
03143    ast_cli_unregister_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
03144 
03145    /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
03146    ao2_ref(conference_bridges, -1);
03147 
03148    conf_destroy_config();
03149 
03150    ast_channel_unregister(&record_tech);
03151    record_tech.capabilities = ast_format_cap_destroy(record_tech.capabilities);
03152 
03153    res |= ast_manager_unregister("ConfbridgeList");
03154    res |= ast_manager_unregister("ConfbridgeListRooms");
03155    res |= ast_manager_unregister("ConfbridgeMute");
03156    res |= ast_manager_unregister("ConfbridgeUnmute");
03157    res |= ast_manager_unregister("ConfbridgeKick");
03158    res |= ast_manager_unregister("ConfbridgeUnlock");
03159    res |= ast_manager_unregister("ConfbridgeLock");
03160    res |= ast_manager_unregister("ConfbridgeStartRecord");
03161    res |= ast_manager_unregister("ConfbridgeStopRecord");
03162    res |= ast_manager_unregister("ConfbridgeSetSingleVideoSrc");
03163 
03164    return res;
03165 }
03166 
03167 /*! \brief Called when module is being loaded */
03168 static int load_module(void)
03169 {
03170    int res = 0;
03171 
03172    if (conf_load_config()) {
03173       ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
03174       return AST_MODULE_LOAD_DECLINE;
03175    }
03176    if ((ast_custom_function_register(&confbridge_function))) {
03177       return AST_MODULE_LOAD_FAILURE;
03178    }
03179    if ((ast_custom_function_register(&confbridge_info_function))) {
03180       return AST_MODULE_LOAD_FAILURE;
03181    }
03182    if (!(record_tech.capabilities = ast_format_cap_alloc())) {
03183       return AST_MODULE_LOAD_FAILURE;
03184    }
03185    ast_format_cap_add_all(record_tech.capabilities);
03186    if (ast_channel_register(&record_tech)) {
03187       ast_log(LOG_ERROR, "Unable to register ConfBridge recorder.\n");
03188       return AST_MODULE_LOAD_FAILURE;
03189    }
03190    /* Create a container to hold the conference bridges */
03191    if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
03192       return AST_MODULE_LOAD_FAILURE;
03193    }
03194    if (ast_register_application_xml(app, confbridge_exec)) {
03195       ao2_ref(conference_bridges, -1);
03196       return AST_MODULE_LOAD_FAILURE;
03197    }
03198 
03199    res |= ast_cli_register_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
03200    res |= ast_manager_register_xml("ConfbridgeList", EVENT_FLAG_REPORTING, action_confbridgelist);
03201    res |= ast_manager_register_xml("ConfbridgeListRooms", EVENT_FLAG_REPORTING, action_confbridgelistrooms);
03202    res |= ast_manager_register_xml("ConfbridgeMute", EVENT_FLAG_CALL, action_confbridgemute);
03203    res |= ast_manager_register_xml("ConfbridgeUnmute", EVENT_FLAG_CALL, action_confbridgeunmute);
03204    res |= ast_manager_register_xml("ConfbridgeKick", EVENT_FLAG_CALL, action_confbridgekick);
03205    res |= ast_manager_register_xml("ConfbridgeUnlock", EVENT_FLAG_CALL, action_confbridgeunlock);
03206    res |= ast_manager_register_xml("ConfbridgeLock", EVENT_FLAG_CALL, action_confbridgelock);
03207    res |= ast_manager_register_xml("ConfbridgeStartRecord", EVENT_FLAG_CALL, action_confbridgestartrecord);
03208    res |= ast_manager_register_xml("ConfbridgeStopRecord", EVENT_FLAG_CALL, action_confbridgestoprecord);
03209    res |= ast_manager_register_xml("ConfbridgeSetSingleVideoSrc", EVENT_FLAG_CALL, action_confbridgesetsinglevideosrc);
03210    if (res) {
03211       return AST_MODULE_LOAD_FAILURE;
03212    }
03213 
03214    return AST_MODULE_LOAD_SUCCESS;
03215 }
03216 
03217 static int reload(void)
03218 {
03219    return conf_reload_config();
03220 }
03221 
03222 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Conference Bridge Application",
03223    .load = load_module,
03224    .unload = unload_module,
03225    .reload = reload,
03226    .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
03227 );