Sat Feb 11 06:33:00 2012

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

Generated on Sat Feb 11 06:33:00 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6