00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "asterisk.h"
00030
00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 324306 $")
00032
00033 #include <stdio.h>
00034 #include <stdlib.h>
00035 #include <unistd.h>
00036 #include <string.h>
00037 #include <signal.h>
00038
00039 #include "asterisk/file.h"
00040 #include "asterisk/logger.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/pbx.h"
00043 #include "asterisk/module.h"
00044 #include "asterisk/lock.h"
00045 #include "asterisk/app.h"
00046 #include "asterisk/bridging.h"
00047 #include "asterisk/musiconhold.h"
00048 #include "asterisk/say.h"
00049 #include "asterisk/audiohook.h"
00050 #include "asterisk/astobj2.h"
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115 static const char *app = "ConfBridge";
00116
00117 enum {
00118 OPTION_ADMIN = (1 << 0),
00119 OPTION_MENU = (1 << 1),
00120 OPTION_MUSICONHOLD = (1 << 2),
00121 OPTION_NOONLYPERSON = (1 << 3),
00122 OPTION_STARTMUTED = (1 << 4),
00123 OPTION_ANNOUNCEUSERCOUNT = (1 << 5),
00124 OPTION_MARKEDUSER = (1 << 6),
00125 OPTION_WAITMARKED = (1 << 7),
00126 OPTION_QUIET = (1 << 8),
00127 };
00128
00129 enum {
00130 OPTION_MUSICONHOLD_CLASS,
00131
00132 OPTION_ARRAY_SIZE,
00133 };
00134
00135 AST_APP_OPTIONS(app_opts,{
00136 AST_APP_OPTION('A', OPTION_MARKEDUSER),
00137 AST_APP_OPTION('a', OPTION_ADMIN),
00138 AST_APP_OPTION('c', OPTION_ANNOUNCEUSERCOUNT),
00139 AST_APP_OPTION('m', OPTION_STARTMUTED),
00140 AST_APP_OPTION_ARG('M', OPTION_MUSICONHOLD, OPTION_MUSICONHOLD_CLASS),
00141 AST_APP_OPTION('1', OPTION_NOONLYPERSON),
00142 AST_APP_OPTION('s', OPTION_MENU),
00143 AST_APP_OPTION('w', OPTION_WAITMARKED),
00144 AST_APP_OPTION('q', OPTION_QUIET),
00145 });
00146
00147
00148 #define MAX_CONF_NAME 32
00149
00150
00151 #define CONFERENCE_BRIDGE_BUCKETS 53
00152
00153
00154 struct conference_bridge {
00155 char name[MAX_CONF_NAME];
00156 struct ast_bridge *bridge;
00157 unsigned int users;
00158 unsigned int markedusers;
00159 unsigned int locked:1;
00160 AST_LIST_HEAD_NOLOCK(, conference_bridge_user) users_list;
00161 struct ast_channel *playback_chan;
00162 ast_mutex_t playback_lock;
00163 };
00164
00165
00166 struct conference_bridge_user {
00167 struct conference_bridge *conference_bridge;
00168 struct ast_channel *chan;
00169 struct ast_flags flags;
00170 char *opt_args[OPTION_ARRAY_SIZE];
00171 struct ast_bridge_features features;
00172 unsigned int kicked:1;
00173 AST_LIST_ENTRY(conference_bridge_user) list;
00174 };
00175
00176
00177 static struct ao2_container *conference_bridges;
00178
00179 static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename);
00180
00181
00182 static int conference_bridge_hash_cb(const void *obj, const int flags)
00183 {
00184 const struct conference_bridge *conference_bridge = obj;
00185 return ast_str_case_hash(conference_bridge->name);
00186 }
00187
00188
00189 static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
00190 {
00191 const struct conference_bridge *conference_bridge0 = obj, *conference_bridge1 = arg;
00192 return (!strcasecmp(conference_bridge0->name, conference_bridge1->name) ? CMP_MATCH | CMP_STOP : 0);
00193 }
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203 static int announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
00204 {
00205 if (conference_bridge->users == 1) {
00206
00207 return 0;
00208 } else if (conference_bridge->users == 2) {
00209
00210 if (ast_stream_and_wait(conference_bridge_user->chan, "conf-onlyone", "")) {
00211 return -1;
00212 }
00213 } else {
00214
00215 if (ast_stream_and_wait(conference_bridge_user->chan, "conf-thereare", "")) {
00216 return -1;
00217 }
00218 if (ast_say_number(conference_bridge_user->chan, conference_bridge->users - 1, "", conference_bridge_user->chan->language, NULL)) {
00219 return -1;
00220 }
00221 if (ast_stream_and_wait(conference_bridge_user->chan, "conf-otherinparty", "")) {
00222 return -1;
00223 }
00224 }
00225 return 0;
00226 }
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239 static int play_prompt_to_channel(struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file)
00240 {
00241 int res;
00242 ao2_unlock(conference_bridge);
00243 res = ast_stream_and_wait(chan, file, "");
00244 ao2_lock(conference_bridge);
00245 return res;
00246 }
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256 static int post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
00257 {
00258 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
00259 struct conference_bridge_user *other_conference_bridge_user = NULL;
00260
00261
00262 if (conference_bridge->markedusers >= 2) {
00263 return 0;
00264 }
00265
00266
00267 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
00268 if (other_conference_bridge_user == conference_bridge_user) {
00269 continue;
00270 }
00271 if (ast_test_flag(&other_conference_bridge_user->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan)) {
00272 ast_moh_stop(other_conference_bridge_user->chan);
00273 ast_bridge_unsuspend(conference_bridge->bridge, other_conference_bridge_user->chan);
00274 }
00275 }
00276
00277
00278 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
00279 ao2_unlock(conference_bridge);
00280 ast_autoservice_start(conference_bridge_user->chan);
00281 play_sound_file(conference_bridge, "conf-placeintoconf");
00282 ast_autoservice_stop(conference_bridge_user->chan);
00283 ao2_lock(conference_bridge);
00284 }
00285
00286
00287 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
00288 if (other_conference_bridge_user == conference_bridge_user) {
00289 continue;
00290 }
00291 other_conference_bridge_user->features.mute = 0;
00292 }
00293
00294 } else {
00295
00296 if (conference_bridge->markedusers) {
00297 return 0;
00298 }
00299
00300 conference_bridge_user->features.mute = 1;
00301
00302 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
00303 if (play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-waitforleader")) {
00304
00305 return -1;
00306 }
00307 }
00308
00309
00310
00311
00312 if (!conference_bridge->markedusers && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
00313 ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00314 }
00315 }
00316 return 0;
00317 }
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327 static int post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
00328 {
00329
00330 if (conference_bridge->users == 1) {
00331
00332 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET | OPTION_NOONLYPERSON)) {
00333 if (play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-onlyperson")) {
00334
00335 return -1;
00336 }
00337 }
00338
00339
00340
00341
00342 if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
00343 ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00344 }
00345 return 0;
00346 }
00347
00348
00349 if (ast_test_flag(&conference_bridge_user->flags, OPTION_ANNOUNCEUSERCOUNT)) {
00350 ao2_unlock(conference_bridge);
00351 if (announce_user_count(conference_bridge, conference_bridge_user)) {
00352 ao2_lock(conference_bridge);
00353 return -1;
00354 }
00355 ao2_lock(conference_bridge);
00356 }
00357
00358
00359 if (conference_bridge->users == 2) {
00360 struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
00361
00362
00363 if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
00364 ast_moh_stop(first_participant->chan);
00365 ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
00366 }
00367 }
00368 return 0;
00369 }
00370
00371
00372
00373
00374
00375
00376
00377
00378 static void destroy_conference_bridge(void *obj)
00379 {
00380 struct conference_bridge *conference_bridge = obj;
00381
00382 ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name);
00383
00384 ast_mutex_destroy(&conference_bridge->playback_lock);
00385
00386 if (conference_bridge->playback_chan) {
00387 struct ast_channel *underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
00388 ast_hangup(underlying_channel);
00389 ast_hangup(conference_bridge->playback_chan);
00390 conference_bridge->playback_chan = NULL;
00391 }
00392
00393
00394 if (conference_bridge->bridge) {
00395 ast_bridge_destroy(conference_bridge->bridge);
00396 conference_bridge->bridge = NULL;
00397 }
00398 }
00399
00400 static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user);
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410 static struct conference_bridge *join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user)
00411 {
00412 struct conference_bridge *conference_bridge = NULL;
00413 struct conference_bridge tmp;
00414
00415 ast_copy_string(tmp.name, name, sizeof(tmp.name));
00416
00417
00418 ao2_lock(conference_bridges);
00419
00420 ast_debug(1, "Trying to find conference bridge '%s'\n", name);
00421
00422
00423 conference_bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
00424
00425
00426 if (conference_bridge && conference_bridge->locked && !ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN)) {
00427 ao2_unlock(conference_bridges);
00428 ao2_ref(conference_bridge, -1);
00429 ast_debug(1, "Conference bridge '%s' is locked and caller is not an admin\n", name);
00430 ast_stream_and_wait(conference_bridge_user->chan, "conf-locked", "");
00431 return NULL;
00432 }
00433
00434
00435 if (!conference_bridge) {
00436
00437 if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) {
00438 ao2_unlock(conference_bridges);
00439 ast_log(LOG_ERROR, "Conference bridge '%s' does not exist.\n", name);
00440 return NULL;
00441 }
00442
00443
00444 ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
00445
00446
00447 if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_SMART))) {
00448 ao2_ref(conference_bridge, -1);
00449 conference_bridge = NULL;
00450 ao2_unlock(conference_bridges);
00451 ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name);
00452 return NULL;
00453 }
00454
00455
00456 ast_mutex_init(&conference_bridge->playback_lock);
00457
00458
00459 ao2_link(conference_bridges, conference_bridge);
00460
00461 ast_debug(1, "Created conference bridge '%s' and linked to container '%p'\n", name, conference_bridges);
00462 }
00463
00464 ao2_unlock(conference_bridges);
00465
00466
00467 conference_bridge_user->conference_bridge = conference_bridge;
00468
00469 ao2_lock(conference_bridge);
00470
00471
00472 AST_LIST_INSERT_TAIL(&conference_bridge->users_list, conference_bridge_user, list);
00473
00474
00475 conference_bridge->users++;
00476
00477
00478 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
00479 conference_bridge->markedusers++;
00480 }
00481
00482
00483 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER | OPTION_WAITMARKED)) {
00484 if (post_join_marked(conference_bridge, conference_bridge_user)) {
00485 ao2_unlock(conference_bridge);
00486 leave_conference_bridge(conference_bridge, conference_bridge_user);
00487 return NULL;
00488 }
00489 } else {
00490 if (post_join_unmarked(conference_bridge, conference_bridge_user)) {
00491 ao2_unlock(conference_bridge);
00492 leave_conference_bridge(conference_bridge, conference_bridge_user);
00493 return NULL;
00494 }
00495 }
00496
00497 ao2_unlock(conference_bridge);
00498
00499 return conference_bridge;
00500 }
00501
00502
00503
00504
00505
00506
00507
00508
00509 static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
00510 {
00511 ao2_lock(conference_bridge);
00512
00513
00514 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
00515 conference_bridge->markedusers--;
00516 }
00517
00518
00519 conference_bridge->users--;
00520
00521
00522 AST_LIST_REMOVE(&conference_bridge->users_list, conference_bridge_user, list);
00523
00524
00525 if (conference_bridge->users) {
00526 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER) && !conference_bridge->markedusers) {
00527 struct conference_bridge_user *other_participant = NULL;
00528
00529
00530 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
00531 other_participant->features.mute = 1;
00532 }
00533
00534
00535 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
00536 ao2_unlock(conference_bridge);
00537 ast_autoservice_start(conference_bridge_user->chan);
00538 play_sound_file(conference_bridge, "conf-leaderhasleft");
00539 ast_autoservice_stop(conference_bridge_user->chan);
00540 ao2_lock(conference_bridge);
00541 }
00542
00543
00544 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
00545 if (ast_test_flag(&other_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) {
00546 ast_moh_start(other_participant->chan, other_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00547 ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan);
00548 }
00549 }
00550 } else if (conference_bridge->users == 1) {
00551
00552 struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
00553
00554 if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
00555 ast_moh_start(first_participant->chan, first_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00556 ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
00557 }
00558 }
00559 } else {
00560 ao2_unlink(conference_bridges, conference_bridge);
00561 }
00562
00563
00564 ao2_unlock(conference_bridge);
00565
00566 ao2_ref(conference_bridge, -1);
00567 }
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578 static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename)
00579 {
00580 struct ast_channel *underlying_channel;
00581
00582 ast_mutex_lock(&conference_bridge->playback_lock);
00583
00584 if (!(conference_bridge->playback_chan)) {
00585 int cause;
00586
00587 if (!(conference_bridge->playback_chan = ast_request("Bridge", AST_FORMAT_SLINEAR, "", &cause))) {
00588 ast_mutex_unlock(&conference_bridge->playback_lock);
00589 return -1;
00590 }
00591
00592 conference_bridge->playback_chan->bridge = conference_bridge->bridge;
00593
00594 if (ast_call(conference_bridge->playback_chan, "", 0)) {
00595 ast_hangup(conference_bridge->playback_chan);
00596 conference_bridge->playback_chan = NULL;
00597 ast_mutex_unlock(&conference_bridge->playback_lock);
00598 return -1;
00599 }
00600
00601 ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name);
00602
00603 underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
00604 } else {
00605
00606 underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
00607 ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL);
00608 }
00609
00610
00611 ast_stream_and_wait(conference_bridge->playback_chan, filename, "");
00612
00613 ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", underlying_channel->name, conference_bridge->bridge);
00614 ast_bridge_depart(conference_bridge->bridge, underlying_channel);
00615
00616 ast_mutex_unlock(&conference_bridge->playback_lock);
00617
00618 return 0;
00619 }
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631 static int menu_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
00632 {
00633 struct conference_bridge_user *conference_bridge_user = hook_pvt;
00634 struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge;
00635 int digit, res = 0, isadmin = ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN);
00636
00637
00638 ao2_lock(conference_bridge);
00639 if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
00640
00641 ast_moh_stop(bridge_channel->chan);
00642 }
00643 ao2_unlock(conference_bridge);
00644
00645
00646 if (ast_streamfile(bridge_channel->chan, (isadmin ? "conf-adminmenu" : "conf-usermenu"), bridge_channel->chan->language)) {
00647 res = -1;
00648 goto finished;
00649 }
00650
00651
00652 digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY);
00653 ast_stopstream(bridge_channel->chan);
00654
00655 if (digit == '1') {
00656
00657 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_WAITMARKED) || conference_bridge->markedusers) {
00658 conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0);
00659 }
00660 res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge_user->features.mute ? "conf-muted" : "conf-unmuted"), "");
00661 } else if (isadmin && digit == '2') {
00662
00663 conference_bridge->locked = (!conference_bridge->locked ? 1 : 0);
00664 res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge->locked ? "conf-lockednow" : "conf-unlockednow"), "");
00665 } else if (isadmin && digit == '3') {
00666
00667 struct conference_bridge_user *last_participant = NULL;
00668
00669 ao2_lock(conference_bridge);
00670 if (((last_participant = AST_LIST_LAST(&conference_bridge->users_list)) == conference_bridge_user) || (ast_test_flag(&last_participant->flags, OPTION_ADMIN))) {
00671 ao2_unlock(conference_bridge);
00672 res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
00673 } else {
00674 last_participant->kicked = 1;
00675 ast_bridge_remove(conference_bridge->bridge, last_participant->chan);
00676 ao2_unlock(conference_bridge);
00677 }
00678 } else if (digit == '4') {
00679
00680 ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, -1);
00681 } else if (digit == '6') {
00682
00683 ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 1);
00684 } else if (digit == '7') {
00685
00686 ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, -1);
00687 } else if (digit == '8') {
00688
00689 } else if (digit == '9') {
00690
00691 ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 1);
00692 } else {
00693
00694 res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
00695 }
00696
00697 finished:
00698
00699 ao2_lock(conference_bridge);
00700 if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
00701 ast_moh_start(bridge_channel->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00702 }
00703 ao2_unlock(conference_bridge);
00704
00705 bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
00706
00707 return res;
00708 }
00709
00710
00711 static int confbridge_exec(struct ast_channel *chan, void *data)
00712 {
00713 int res = 0, volume_adjustments[2];
00714 char *parse;
00715 struct conference_bridge *conference_bridge = NULL;
00716 struct conference_bridge_user conference_bridge_user = {
00717 .chan = chan,
00718 };
00719 const char *tmp, *join_sound = NULL, *leave_sound = NULL;
00720 AST_DECLARE_APP_ARGS(args,
00721 AST_APP_ARG(conf_name);
00722 AST_APP_ARG(options);
00723 );
00724
00725 if (ast_strlen_zero(data)) {
00726 ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
00727 return -1;
00728 }
00729
00730
00731 parse = ast_strdupa(data);
00732
00733 AST_STANDARD_APP_ARGS(args, parse);
00734
00735 if (args.argc == 2) {
00736 ast_app_parse_options(app_opts, &conference_bridge_user.flags, conference_bridge_user.opt_args, args.options);
00737 }
00738
00739
00740 if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) {
00741 return -1;
00742 }
00743
00744
00745 volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
00746 volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
00747
00748
00749 ast_bridge_features_init(&conference_bridge_user.features);
00750
00751
00752 if (ast_test_flag(&conference_bridge_user.flags, OPTION_MENU)) {
00753 ast_bridge_features_hook(&conference_bridge_user.features, "#", menu_callback, &conference_bridge_user);
00754 }
00755
00756
00757 if (ast_test_flag(&conference_bridge_user.flags, OPTION_STARTMUTED)) {
00758 conference_bridge_user.features.mute = 1;
00759 }
00760
00761
00762 ast_channel_lock(chan);
00763 if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_JOIN_SOUND"))) {
00764 join_sound = ast_strdupa(tmp);
00765 }
00766 if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_LEAVE_SOUND"))) {
00767 leave_sound = ast_strdupa(tmp);
00768 }
00769 ast_channel_unlock(chan);
00770
00771
00772 if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(join_sound) && conference_bridge->users >= 2) {
00773 ast_autoservice_start(chan);
00774 play_sound_file(conference_bridge, join_sound);
00775 ast_autoservice_stop(chan);
00776 }
00777
00778
00779 ast_bridge_join(conference_bridge->bridge, chan, NULL, &conference_bridge_user.features);
00780
00781
00782 if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(leave_sound) && conference_bridge->users >= 2) {
00783 ast_autoservice_start(chan);
00784 play_sound_file(conference_bridge, leave_sound);
00785 ast_autoservice_stop(chan);
00786 }
00787
00788
00789 leave_conference_bridge(conference_bridge, &conference_bridge_user);
00790 conference_bridge = NULL;
00791
00792
00793 ast_bridge_features_cleanup(&conference_bridge_user.features);
00794
00795
00796 if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && conference_bridge_user.kicked) {
00797 res = ast_stream_and_wait(chan, "conf-kicked", "");
00798 }
00799
00800
00801 if (volume_adjustments[0]) {
00802 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
00803 }
00804 if (volume_adjustments[1]) {
00805 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
00806 }
00807
00808 return res;
00809 }
00810
00811
00812 static int unload_module(void)
00813 {
00814 int res = ast_unregister_application(app);
00815
00816
00817 ao2_ref(conference_bridges, -1);
00818
00819 return res;
00820 }
00821
00822
00823 static int load_module(void)
00824 {
00825
00826 if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
00827 return AST_MODULE_LOAD_DECLINE;
00828 }
00829
00830 if (ast_register_application_xml(app, confbridge_exec)) {
00831 ao2_ref(conference_bridges, -1);
00832 return AST_MODULE_LOAD_DECLINE;
00833 }
00834
00835 return AST_MODULE_LOAD_SUCCESS;
00836 }
00837
00838 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Conference Bridge Application");