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