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: 278132 $")
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 (conference_bridge->users == 1) {
00468 ast_devstate_changed(AST_DEVICE_INUSE, "confbridge:%s", conference_bridge->name);
00469 }
00470
00471
00472 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER | OPTION_WAITMARKED)) {
00473 post_join_marked(conference_bridge, conference_bridge_user);
00474 } else {
00475 post_join_unmarked(conference_bridge, conference_bridge_user);
00476 }
00477
00478 ao2_unlock(conference_bridge);
00479
00480 return conference_bridge;
00481 }
00482
00483
00484
00485
00486
00487
00488
00489
00490 static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
00491 {
00492 ao2_lock(conference_bridge);
00493
00494
00495 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
00496 conference_bridge->markedusers--;
00497 }
00498
00499
00500 conference_bridge->users--;
00501
00502
00503 AST_LIST_REMOVE(&conference_bridge->users_list, conference_bridge_user, list);
00504
00505
00506 if (conference_bridge->users) {
00507 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER) && !conference_bridge->markedusers) {
00508 struct conference_bridge_user *other_participant = NULL;
00509
00510
00511 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
00512 other_participant->features.mute = 1;
00513 }
00514
00515
00516 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
00517 ao2_unlock(conference_bridge);
00518 ast_autoservice_start(conference_bridge_user->chan);
00519 play_sound_file(conference_bridge, "conf-leaderhasleft");
00520 ast_autoservice_stop(conference_bridge_user->chan);
00521 ao2_lock(conference_bridge);
00522 }
00523
00524
00525 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
00526 if (ast_test_flag(&other_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) {
00527 ast_moh_start(other_participant->chan, other_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00528 ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan);
00529 }
00530 }
00531 } else if (conference_bridge->users == 1) {
00532
00533 struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
00534
00535 if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
00536 ast_moh_start(first_participant->chan, first_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00537 ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
00538 }
00539 }
00540 } else {
00541
00542 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "confbridge:%s", conference_bridge->name);
00543
00544 ao2_unlink(conference_bridges, conference_bridge);
00545 }
00546
00547
00548 ao2_unlock(conference_bridge);
00549
00550 ao2_ref(conference_bridge, -1);
00551 }
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562 static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename)
00563 {
00564 struct ast_channel *underlying_channel;
00565
00566 ast_mutex_lock(&conference_bridge->playback_lock);
00567
00568 if (!(conference_bridge->playback_chan)) {
00569 int cause;
00570
00571 if (!(conference_bridge->playback_chan = ast_request("Bridge", AST_FORMAT_SLINEAR, NULL, "", &cause))) {
00572 ast_mutex_unlock(&conference_bridge->playback_lock);
00573 return -1;
00574 }
00575
00576 conference_bridge->playback_chan->bridge = conference_bridge->bridge;
00577
00578 if (ast_call(conference_bridge->playback_chan, "", 0)) {
00579 ast_hangup(conference_bridge->playback_chan);
00580 conference_bridge->playback_chan = NULL;
00581 ast_mutex_unlock(&conference_bridge->playback_lock);
00582 return -1;
00583 }
00584
00585 ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name);
00586
00587 underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
00588 } else {
00589
00590 underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
00591 ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL);
00592 }
00593
00594
00595 ast_stream_and_wait(conference_bridge->playback_chan, filename, "");
00596
00597 ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", underlying_channel->name, conference_bridge->bridge);
00598 ast_bridge_depart(conference_bridge->bridge, underlying_channel);
00599
00600 ast_mutex_unlock(&conference_bridge->playback_lock);
00601
00602 return 0;
00603 }
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615 static int menu_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
00616 {
00617 struct conference_bridge_user *conference_bridge_user = hook_pvt;
00618 struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge;
00619 int digit, res = 0, isadmin = ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN);
00620
00621
00622 ao2_lock(conference_bridge);
00623 if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
00624
00625 ast_moh_stop(bridge_channel->chan);
00626 }
00627 ao2_unlock(conference_bridge);
00628
00629
00630 if (ast_streamfile(bridge_channel->chan, (isadmin ? "conf-adminmenu" : "conf-usermenu"), bridge_channel->chan->language)) {
00631 res = -1;
00632 goto finished;
00633 }
00634
00635
00636 digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY);
00637 ast_stopstream(bridge_channel->chan);
00638
00639 if (digit == '1') {
00640
00641 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_WAITMARKED) || conference_bridge->markedusers) {
00642 conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0);
00643 }
00644 res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge_user->features.mute ? "conf-muted" : "conf-unmuted"), "");
00645 } else if (isadmin && digit == '2') {
00646
00647 conference_bridge->locked = (!conference_bridge->locked ? 1 : 0);
00648 res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge->locked ? "conf-lockednow" : "conf-unlockednow"), "");
00649 } else if (isadmin && digit == '3') {
00650
00651 struct conference_bridge_user *last_participant = NULL;
00652
00653 ao2_lock(conference_bridge);
00654 if (((last_participant = AST_LIST_LAST(&conference_bridge->users_list)) == conference_bridge_user) || (ast_test_flag(&last_participant->flags, OPTION_ADMIN))) {
00655 ao2_unlock(conference_bridge);
00656 res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
00657 } else {
00658 last_participant->kicked = 1;
00659 ast_bridge_remove(conference_bridge->bridge, last_participant->chan);
00660 ao2_unlock(conference_bridge);
00661 }
00662 } else if (digit == '4') {
00663
00664 ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, -1);
00665 } else if (digit == '6') {
00666
00667 ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 1);
00668 } else if (digit == '7') {
00669
00670 ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, -1);
00671 } else if (digit == '8') {
00672
00673 } else if (digit == '9') {
00674
00675 ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 1);
00676 } else {
00677
00678 res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
00679 }
00680
00681 finished:
00682
00683 ao2_lock(conference_bridge);
00684 if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
00685 ast_moh_start(bridge_channel->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00686 }
00687 ao2_unlock(conference_bridge);
00688
00689 bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
00690
00691 return res;
00692 }
00693
00694
00695 static int confbridge_exec(struct ast_channel *chan, const char *data)
00696 {
00697 int res = 0, volume_adjustments[2];
00698 char *parse;
00699 struct conference_bridge *conference_bridge = NULL;
00700 struct conference_bridge_user conference_bridge_user = {
00701 .chan = chan,
00702 };
00703 const char *tmp, *join_sound = NULL, *leave_sound = NULL;
00704 AST_DECLARE_APP_ARGS(args,
00705 AST_APP_ARG(conf_name);
00706 AST_APP_ARG(options);
00707 );
00708
00709 if (ast_strlen_zero(data)) {
00710 ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
00711 return -1;
00712 }
00713
00714
00715 parse = ast_strdupa(data);
00716
00717 AST_STANDARD_APP_ARGS(args, parse);
00718
00719 if (args.argc == 2) {
00720 ast_app_parse_options(app_opts, &conference_bridge_user.flags, conference_bridge_user.opt_args, args.options);
00721 }
00722
00723
00724 if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) {
00725 return -1;
00726 }
00727
00728
00729 volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
00730 volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
00731
00732
00733 ast_bridge_features_init(&conference_bridge_user.features);
00734
00735
00736 if (ast_test_flag(&conference_bridge_user.flags, OPTION_MENU)) {
00737 ast_bridge_features_hook(&conference_bridge_user.features, "#", menu_callback, &conference_bridge_user);
00738 }
00739
00740
00741 if (ast_test_flag(&conference_bridge_user.flags, OPTION_STARTMUTED)) {
00742 conference_bridge_user.features.mute = 1;
00743 }
00744
00745
00746 ast_channel_lock(chan);
00747 if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_JOIN_SOUND"))) {
00748 join_sound = ast_strdupa(tmp);
00749 }
00750 if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_LEAVE_SOUND"))) {
00751 leave_sound = ast_strdupa(tmp);
00752 }
00753 ast_channel_unlock(chan);
00754
00755
00756 if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(join_sound) && conference_bridge->users >= 2) {
00757 ast_autoservice_start(chan);
00758 play_sound_file(conference_bridge, join_sound);
00759 ast_autoservice_stop(chan);
00760 }
00761
00762
00763 ast_bridge_join(conference_bridge->bridge, chan, NULL, &conference_bridge_user.features);
00764
00765
00766 if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(leave_sound) && conference_bridge->users >= 2) {
00767 ast_autoservice_start(chan);
00768 play_sound_file(conference_bridge, leave_sound);
00769 ast_autoservice_stop(chan);
00770 }
00771
00772
00773 leave_conference_bridge(conference_bridge, &conference_bridge_user);
00774 conference_bridge = NULL;
00775
00776
00777 ast_bridge_features_cleanup(&conference_bridge_user.features);
00778
00779
00780 if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && conference_bridge_user.kicked) {
00781 res = ast_stream_and_wait(chan, "conf-kicked", "");
00782 }
00783
00784
00785 if (volume_adjustments[0]) {
00786 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
00787 }
00788 if (volume_adjustments[1]) {
00789 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
00790 }
00791
00792 return res;
00793 }
00794
00795
00796 static int unload_module(void)
00797 {
00798 int res = ast_unregister_application(app);
00799
00800
00801 ao2_ref(conference_bridges, -1);
00802
00803 return res;
00804 }
00805
00806
00807 static int load_module(void)
00808 {
00809
00810 if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
00811 return AST_MODULE_LOAD_DECLINE;
00812 }
00813
00814 if (ast_register_application_xml(app, confbridge_exec)) {
00815 ao2_ref(conference_bridges, -1);
00816 return AST_MODULE_LOAD_DECLINE;
00817 }
00818
00819 return AST_MODULE_LOAD_SUCCESS;
00820 }
00821
00822 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Conference Bridge Application",
00823 .load = load_module,
00824 .unload = unload_module,
00825 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
00826 );