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: 249894 $")
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 void 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;
00208 } else if (conference_bridge->users == 2) {
00209
00210 if (ast_stream_and_wait(conference_bridge_user->chan, "conf-onlyone", "")) {
00211 return;
00212 }
00213 } else {
00214
00215 if (ast_stream_and_wait(conference_bridge_user->chan, "conf-thereare", "")) {
00216 return;
00217 }
00218 if (ast_say_number(conference_bridge_user->chan, conference_bridge->users - 1, "", conference_bridge_user->chan->language, NULL)) {
00219 return;
00220 }
00221 if (ast_stream_and_wait(conference_bridge_user->chan, "conf-otherinparty", "")) {
00222 return;
00223 }
00224 }
00225 }
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238 static void play_prompt_to_channel(struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file)
00239 {
00240 ao2_unlock(conference_bridge);
00241 ast_stream_and_wait(chan, file, "");
00242 ao2_lock(conference_bridge);
00243 }
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253 static void post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
00254 {
00255 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
00256 struct conference_bridge_user *other_conference_bridge_user = NULL;
00257
00258
00259 if (conference_bridge->markedusers >= 2) {
00260 return;
00261 }
00262
00263
00264 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
00265 if (other_conference_bridge_user == conference_bridge_user) {
00266 continue;
00267 }
00268 if (ast_test_flag(&other_conference_bridge_user->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan)) {
00269 ast_moh_stop(other_conference_bridge_user->chan);
00270 ast_bridge_unsuspend(conference_bridge->bridge, other_conference_bridge_user->chan);
00271 }
00272 }
00273
00274
00275 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
00276 ao2_unlock(conference_bridge);
00277 ast_autoservice_start(conference_bridge_user->chan);
00278 play_sound_file(conference_bridge, "conf-placeintoconf");
00279 ast_autoservice_stop(conference_bridge_user->chan);
00280 ao2_lock(conference_bridge);
00281 }
00282
00283
00284 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
00285 if (other_conference_bridge_user == conference_bridge_user) {
00286 continue;
00287 }
00288 other_conference_bridge_user->features.mute = 0;
00289 }
00290
00291 } else {
00292
00293 if (conference_bridge->markedusers) {
00294 return;
00295 }
00296
00297 conference_bridge_user->features.mute = 1;
00298
00299 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
00300 play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-waitforleader");
00301 }
00302
00303
00304
00305
00306 if (!conference_bridge->markedusers && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
00307 ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00308 }
00309 }
00310 }
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320 static void post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
00321 {
00322
00323 if (conference_bridge->users == 1) {
00324
00325 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET | OPTION_NOONLYPERSON)) {
00326 play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-onlyperson");
00327 }
00328
00329
00330
00331
00332 if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
00333 ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00334 }
00335 return;
00336 }
00337
00338
00339 if (ast_test_flag(&conference_bridge_user->flags, OPTION_ANNOUNCEUSERCOUNT)) {
00340 ao2_unlock(conference_bridge);
00341 announce_user_count(conference_bridge, conference_bridge_user);
00342 ao2_lock(conference_bridge);
00343 }
00344
00345
00346 if (conference_bridge->users == 2) {
00347 struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
00348
00349
00350 if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
00351 ast_moh_stop(first_participant->chan);
00352 ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
00353 }
00354 }
00355 }
00356
00357
00358
00359
00360
00361
00362
00363
00364 static void destroy_conference_bridge(void *obj)
00365 {
00366 struct conference_bridge *conference_bridge = obj;
00367
00368 ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name);
00369
00370 ast_mutex_destroy(&conference_bridge->playback_lock);
00371
00372 if (conference_bridge->playback_chan) {
00373 struct ast_channel *underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
00374 ast_hangup(underlying_channel);
00375 ast_hangup(conference_bridge->playback_chan);
00376 conference_bridge->playback_chan = NULL;
00377 }
00378
00379
00380 if (conference_bridge->bridge) {
00381 ast_bridge_destroy(conference_bridge->bridge);
00382 conference_bridge->bridge = NULL;
00383 }
00384 }
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394 static struct conference_bridge *join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user)
00395 {
00396 struct conference_bridge *conference_bridge = NULL;
00397 struct conference_bridge tmp;
00398
00399 ast_copy_string(tmp.name, name, sizeof(tmp.name));
00400
00401
00402 ao2_lock(conference_bridges);
00403
00404 ast_debug(1, "Trying to find conference bridge '%s'\n", name);
00405
00406
00407 conference_bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
00408
00409
00410 if (conference_bridge && conference_bridge->locked && !ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN)) {
00411 ao2_unlock(conference_bridges);
00412 ao2_ref(conference_bridge, -1);
00413 ast_debug(1, "Conference bridge '%s' is locked and caller is not an admin\n", name);
00414 ast_stream_and_wait(conference_bridge_user->chan, "conf-locked", "");
00415 return NULL;
00416 }
00417
00418
00419 if (!conference_bridge) {
00420
00421 if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) {
00422 ao2_unlock(conference_bridges);
00423 ast_log(LOG_ERROR, "Conference bridge '%s' does not exist.\n", name);
00424 return NULL;
00425 }
00426
00427
00428 ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
00429
00430
00431 if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_SMART))) {
00432 ao2_ref(conference_bridge, -1);
00433 conference_bridge = NULL;
00434 ao2_unlock(conference_bridges);
00435 ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name);
00436 return NULL;
00437 }
00438
00439
00440 ast_mutex_init(&conference_bridge->playback_lock);
00441
00442
00443 ao2_link(conference_bridges, conference_bridge);
00444
00445 ast_debug(1, "Created conference bridge '%s' and linked to container '%p'\n", name, conference_bridges);
00446 }
00447
00448 ao2_unlock(conference_bridges);
00449
00450
00451 conference_bridge_user->conference_bridge = conference_bridge;
00452
00453 ao2_lock(conference_bridge);
00454
00455
00456 AST_LIST_INSERT_TAIL(&conference_bridge->users_list, conference_bridge_user, list);
00457
00458
00459 conference_bridge->users++;
00460
00461
00462 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
00463 conference_bridge->markedusers++;
00464 }
00465
00466
00467 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER | OPTION_WAITMARKED)) {
00468 post_join_marked(conference_bridge, conference_bridge_user);
00469 } else {
00470 post_join_unmarked(conference_bridge, conference_bridge_user);
00471 }
00472
00473 ao2_unlock(conference_bridge);
00474
00475 return conference_bridge;
00476 }
00477
00478
00479
00480
00481
00482
00483
00484
00485 static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
00486 {
00487 ao2_lock(conference_bridge);
00488
00489
00490 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
00491 conference_bridge->markedusers--;
00492 }
00493
00494
00495 conference_bridge->users--;
00496
00497
00498 AST_LIST_REMOVE(&conference_bridge->users_list, conference_bridge_user, list);
00499
00500
00501 if (conference_bridge->users) {
00502 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER) && !conference_bridge->markedusers) {
00503 struct conference_bridge_user *other_participant = NULL;
00504
00505
00506 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
00507 other_participant->features.mute = 1;
00508 }
00509
00510
00511 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
00512 ao2_unlock(conference_bridge);
00513 ast_autoservice_start(conference_bridge_user->chan);
00514 play_sound_file(conference_bridge, "conf-leaderhasleft");
00515 ast_autoservice_stop(conference_bridge_user->chan);
00516 ao2_lock(conference_bridge);
00517 }
00518
00519
00520 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
00521 if (ast_test_flag(&other_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) {
00522 ast_moh_start(other_participant->chan, other_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00523 ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan);
00524 }
00525 }
00526 } else if (conference_bridge->users == 1) {
00527
00528 struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
00529
00530 if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
00531 ast_moh_start(first_participant->chan, first_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00532 ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
00533 }
00534 }
00535 } else {
00536 ao2_unlink(conference_bridges, conference_bridge);
00537 }
00538
00539
00540 ao2_unlock(conference_bridge);
00541
00542 ao2_ref(conference_bridge, -1);
00543 }
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554 static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename)
00555 {
00556 struct ast_channel *underlying_channel;
00557
00558 ast_mutex_lock(&conference_bridge->playback_lock);
00559
00560 if (!(conference_bridge->playback_chan)) {
00561 int cause;
00562
00563 if (!(conference_bridge->playback_chan = ast_request("Bridge", AST_FORMAT_SLINEAR, "", &cause))) {
00564 ast_mutex_unlock(&conference_bridge->playback_lock);
00565 return -1;
00566 }
00567
00568 conference_bridge->playback_chan->bridge = conference_bridge->bridge;
00569
00570 if (ast_call(conference_bridge->playback_chan, "", 0)) {
00571 ast_hangup(conference_bridge->playback_chan);
00572 conference_bridge->playback_chan = NULL;
00573 ast_mutex_unlock(&conference_bridge->playback_lock);
00574 return -1;
00575 }
00576
00577 ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name);
00578
00579 underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
00580 } else {
00581
00582 underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
00583 ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL);
00584 }
00585
00586
00587 ast_stream_and_wait(conference_bridge->playback_chan, filename, "");
00588
00589 ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", underlying_channel->name, conference_bridge->bridge);
00590 ast_bridge_depart(conference_bridge->bridge, underlying_channel);
00591
00592 ast_mutex_unlock(&conference_bridge->playback_lock);
00593
00594 return 0;
00595 }
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607 static int menu_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
00608 {
00609 struct conference_bridge_user *conference_bridge_user = hook_pvt;
00610 struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge;
00611 int digit, res = 0, isadmin = ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN);
00612
00613
00614 ao2_lock(conference_bridge);
00615 if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
00616
00617 ast_moh_stop(bridge_channel->chan);
00618 }
00619 ao2_unlock(conference_bridge);
00620
00621
00622 if (ast_streamfile(bridge_channel->chan, (isadmin ? "conf-adminmenu" : "conf-usermenu"), bridge_channel->chan->language)) {
00623 res = -1;
00624 goto finished;
00625 }
00626
00627
00628 digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY);
00629 ast_stopstream(bridge_channel->chan);
00630
00631 if (digit == '1') {
00632
00633 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_WAITMARKED) || conference_bridge->markedusers) {
00634 conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0);
00635 }
00636 res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge_user->features.mute ? "conf-muted" : "conf-unmuted"), "");
00637 } else if (isadmin && digit == '2') {
00638
00639 conference_bridge->locked = (!conference_bridge->locked ? 1 : 0);
00640 res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge->locked ? "conf-lockednow" : "conf-unlockednow"), "");
00641 } else if (isadmin && digit == '3') {
00642
00643 struct conference_bridge_user *last_participant = NULL;
00644
00645 ao2_lock(conference_bridge);
00646 if (((last_participant = AST_LIST_LAST(&conference_bridge->users_list)) == conference_bridge_user) || (ast_test_flag(&last_participant->flags, OPTION_ADMIN))) {
00647 ao2_unlock(conference_bridge);
00648 res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
00649 } else {
00650 last_participant->kicked = 1;
00651 ast_bridge_remove(conference_bridge->bridge, last_participant->chan);
00652 ao2_unlock(conference_bridge);
00653 }
00654 } else if (digit == '4') {
00655
00656 ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, -1);
00657 } else if (digit == '6') {
00658
00659 ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 1);
00660 } else if (digit == '7') {
00661
00662 ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, -1);
00663 } else if (digit == '8') {
00664
00665 } else if (digit == '9') {
00666
00667 ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 1);
00668 } else {
00669
00670 res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
00671 }
00672
00673 finished:
00674
00675 ao2_lock(conference_bridge);
00676 if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
00677 ast_moh_start(bridge_channel->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00678 }
00679 ao2_unlock(conference_bridge);
00680
00681 bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
00682
00683 return res;
00684 }
00685
00686
00687 static int confbridge_exec(struct ast_channel *chan, void *data)
00688 {
00689 int res = 0, volume_adjustments[2];
00690 char *parse;
00691 struct conference_bridge *conference_bridge = NULL;
00692 struct conference_bridge_user conference_bridge_user = {
00693 .chan = chan,
00694 };
00695 const char *tmp, *join_sound = NULL, *leave_sound = NULL;
00696 AST_DECLARE_APP_ARGS(args,
00697 AST_APP_ARG(conf_name);
00698 AST_APP_ARG(options);
00699 );
00700
00701 if (ast_strlen_zero(data)) {
00702 ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
00703 return -1;
00704 }
00705
00706
00707 parse = ast_strdupa(data);
00708
00709 AST_STANDARD_APP_ARGS(args, parse);
00710
00711 if (args.argc == 2) {
00712 ast_app_parse_options(app_opts, &conference_bridge_user.flags, conference_bridge_user.opt_args, args.options);
00713 }
00714
00715
00716 if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) {
00717 return -1;
00718 }
00719
00720
00721 volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
00722 volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
00723
00724
00725 ast_bridge_features_init(&conference_bridge_user.features);
00726
00727
00728 if (ast_test_flag(&conference_bridge_user.flags, OPTION_MENU)) {
00729 ast_bridge_features_hook(&conference_bridge_user.features, "#", menu_callback, &conference_bridge_user);
00730 }
00731
00732
00733 if (ast_test_flag(&conference_bridge_user.flags, OPTION_STARTMUTED)) {
00734 conference_bridge_user.features.mute = 1;
00735 }
00736
00737
00738 ast_channel_lock(chan);
00739 if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_JOIN_SOUND"))) {
00740 join_sound = ast_strdupa(tmp);
00741 }
00742 if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_LEAVE_SOUND"))) {
00743 leave_sound = ast_strdupa(tmp);
00744 }
00745 ast_channel_unlock(chan);
00746
00747
00748 if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(join_sound) && conference_bridge->users >= 2) {
00749 ast_autoservice_start(chan);
00750 play_sound_file(conference_bridge, join_sound);
00751 ast_autoservice_stop(chan);
00752 }
00753
00754
00755 ast_bridge_join(conference_bridge->bridge, chan, NULL, &conference_bridge_user.features);
00756
00757
00758 if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(leave_sound) && conference_bridge->users >= 2) {
00759 ast_autoservice_start(chan);
00760 play_sound_file(conference_bridge, leave_sound);
00761 ast_autoservice_stop(chan);
00762 }
00763
00764
00765 leave_conference_bridge(conference_bridge, &conference_bridge_user);
00766 conference_bridge = NULL;
00767
00768
00769 ast_bridge_features_cleanup(&conference_bridge_user.features);
00770
00771
00772 if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && conference_bridge_user.kicked) {
00773 res = ast_stream_and_wait(chan, "conf-kicked", "");
00774 }
00775
00776
00777 if (volume_adjustments[0]) {
00778 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
00779 }
00780 if (volume_adjustments[1]) {
00781 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
00782 }
00783
00784 return res;
00785 }
00786
00787
00788 static int unload_module(void)
00789 {
00790 int res = ast_unregister_application(app);
00791
00792
00793 ao2_ref(conference_bridges, -1);
00794
00795 return res;
00796 }
00797
00798
00799 static int load_module(void)
00800 {
00801
00802 if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
00803 return AST_MODULE_LOAD_DECLINE;
00804 }
00805
00806 if (ast_register_application_xml(app, confbridge_exec)) {
00807 ao2_ref(conference_bridges, -1);
00808 return AST_MODULE_LOAD_DECLINE;
00809 }
00810
00811 return AST_MODULE_LOAD_SUCCESS;
00812 }
00813
00814 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Conference Bridge Application");