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