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
00034
00035 #include "asterisk.h"
00036
00037 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 353685 $")
00038
00039 #include <stdio.h>
00040 #include <stdlib.h>
00041 #include <unistd.h>
00042 #include <string.h>
00043 #include <signal.h>
00044
00045 #include "asterisk/cli.h"
00046 #include "asterisk/file.h"
00047 #include "asterisk/channel.h"
00048 #include "asterisk/pbx.h"
00049 #include "asterisk/pbx.h"
00050 #include "asterisk/module.h"
00051 #include "asterisk/lock.h"
00052 #include "asterisk/bridging.h"
00053 #include "asterisk/musiconhold.h"
00054 #include "asterisk/say.h"
00055 #include "asterisk/audiohook.h"
00056 #include "asterisk/astobj2.h"
00057 #include "confbridge/include/confbridge.h"
00058 #include "asterisk/paths.h"
00059 #include "asterisk/manager.h"
00060 #include "asterisk/test.h"
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
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271 static const char app[] = "ConfBridge";
00272
00273
00274 #define CONFERENCE_BRIDGE_BUCKETS 53
00275
00276
00277 static struct ao2_container *conference_bridges;
00278
00279 static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename);
00280 static int play_sound_number(struct conference_bridge *conference_bridge, int say_number);
00281 static int execute_menu_entry(struct conference_bridge *conference_bridge,
00282 struct conference_bridge_user *conference_bridge_user,
00283 struct ast_bridge_channel *bridge_channel,
00284 struct conf_menu_entry *menu_entry,
00285 struct conf_menu *menu);
00286
00287
00288 static int conference_bridge_hash_cb(const void *obj, const int flags)
00289 {
00290 const struct conference_bridge *conference_bridge = obj;
00291 return ast_str_case_hash(conference_bridge->name);
00292 }
00293
00294
00295 static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
00296 {
00297 const struct conference_bridge *conference_bridge0 = obj, *conference_bridge1 = arg;
00298 return (!strcasecmp(conference_bridge0->name, conference_bridge1->name) ? CMP_MATCH | CMP_STOP : 0);
00299 }
00300
00301 const char *conf_get_sound(enum conf_sounds sound, struct bridge_profile_sounds *custom_sounds)
00302 {
00303 switch (sound) {
00304 case CONF_SOUND_HAS_JOINED:
00305 return S_OR(custom_sounds->hasjoin, "conf-hasjoin");
00306 case CONF_SOUND_HAS_LEFT:
00307 return S_OR(custom_sounds->hasleft, "conf-hasleft");
00308 case CONF_SOUND_KICKED:
00309 return S_OR(custom_sounds->kicked, "conf-kicked");
00310 case CONF_SOUND_MUTED:
00311 return S_OR(custom_sounds->muted, "conf-muted");
00312 case CONF_SOUND_UNMUTED:
00313 return S_OR(custom_sounds->unmuted, "conf-unmuted");
00314 case CONF_SOUND_ONLY_ONE:
00315 return S_OR(custom_sounds->onlyone, "conf-onlyone");
00316 case CONF_SOUND_THERE_ARE:
00317 return S_OR(custom_sounds->thereare, "conf-thereare");
00318 case CONF_SOUND_OTHER_IN_PARTY:
00319 return S_OR(custom_sounds->otherinparty, "conf-otherinparty");
00320 case CONF_SOUND_PLACE_IN_CONF:
00321 return S_OR(custom_sounds->placeintoconf, "conf-placeintoconf");
00322 case CONF_SOUND_WAIT_FOR_LEADER:
00323 return S_OR(custom_sounds->waitforleader, "conf-waitforleader");
00324 case CONF_SOUND_LEADER_HAS_LEFT:
00325 return S_OR(custom_sounds->leaderhasleft, "conf-leaderhasleft");
00326 case CONF_SOUND_GET_PIN:
00327 return S_OR(custom_sounds->getpin, "conf-getpin");
00328 case CONF_SOUND_INVALID_PIN:
00329 return S_OR(custom_sounds->invalidpin, "conf-invalidpin");
00330 case CONF_SOUND_ONLY_PERSON:
00331 return S_OR(custom_sounds->onlyperson, "conf-onlyperson");
00332 case CONF_SOUND_LOCKED:
00333 return S_OR(custom_sounds->locked, "conf-locked");
00334 case CONF_SOUND_LOCKED_NOW:
00335 return S_OR(custom_sounds->lockednow, "conf-lockednow");
00336 case CONF_SOUND_UNLOCKED_NOW:
00337 return S_OR(custom_sounds->unlockednow, "conf-unlockednow");
00338 case CONF_SOUND_ERROR_MENU:
00339 return S_OR(custom_sounds->errormenu, "conf-errormenu");
00340 case CONF_SOUND_JOIN:
00341 return S_OR(custom_sounds->join, "confbridge-join");
00342 case CONF_SOUND_LEAVE:
00343 return S_OR(custom_sounds->leave, "confbridge-leave");
00344 case CONF_SOUND_PARTICIPANTS_MUTED:
00345 return S_OR(custom_sounds->participantsmuted, "conf-now-muted");
00346 case CONF_SOUND_PARTICIPANTS_UNMUTED:
00347 return S_OR(custom_sounds->participantsunmuted, "conf-now-unmuted");
00348 }
00349
00350 return "";
00351 }
00352
00353 static struct ast_frame *rec_read(struct ast_channel *ast)
00354 {
00355 return &ast_null_frame;
00356 }
00357 static int rec_write(struct ast_channel *ast, struct ast_frame *f)
00358 {
00359 return 0;
00360 }
00361 static struct ast_channel *rec_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause);
00362 static struct ast_channel_tech record_tech = {
00363 .type = "ConfBridgeRec",
00364 .description = "Conference Bridge Recording Channel",
00365 .requester = rec_request,
00366 .read = rec_read,
00367 .write = rec_write,
00368 };
00369 static struct ast_channel *rec_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause)
00370 {
00371 struct ast_channel *tmp;
00372 struct ast_format fmt;
00373 const char *conf_name = data;
00374 if (!(tmp = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", NULL, 0,
00375 "ConfBridgeRecorder/conf-%s-uid-%d",
00376 conf_name,
00377 (int) ast_random()))) {
00378 return NULL;
00379 }
00380 ast_format_set(&fmt, AST_FORMAT_SLINEAR, 0);
00381 tmp->tech = &record_tech;
00382 ast_format_cap_add_all(tmp->nativeformats);
00383 ast_format_copy(&tmp->writeformat, &fmt);
00384 ast_format_copy(&tmp->rawwriteformat, &fmt);
00385 ast_format_copy(&tmp->readformat, &fmt);
00386 ast_format_copy(&tmp->rawreadformat, &fmt);
00387 return tmp;
00388 }
00389
00390 static void *record_thread(void *obj)
00391 {
00392 struct conference_bridge *conference_bridge = obj;
00393 struct ast_app *mixmonapp = pbx_findapp("MixMonitor");
00394 struct ast_channel *chan;
00395 struct ast_str *filename = ast_str_alloca(PATH_MAX);
00396
00397 if (!mixmonapp) {
00398 ao2_ref(conference_bridge, -1);
00399 return NULL;
00400 }
00401
00402 ao2_lock(conference_bridge);
00403 if (!(conference_bridge->record_chan)) {
00404 conference_bridge->record_thread = AST_PTHREADT_NULL;
00405 ao2_unlock(conference_bridge);
00406 ao2_ref(conference_bridge, -1);
00407 return NULL;
00408 }
00409 chan = ast_channel_ref(conference_bridge->record_chan);
00410
00411 if (!(ast_strlen_zero(conference_bridge->b_profile.rec_file))) {
00412 ast_str_append(&filename, 0, "%s", conference_bridge->b_profile.rec_file);
00413 } else {
00414 time_t now;
00415 time(&now);
00416 ast_str_append(&filename, 0, "confbridge-%s-%u.wav",
00417 conference_bridge->name,
00418 (unsigned int) now);
00419 }
00420 ao2_unlock(conference_bridge);
00421
00422 ast_answer(chan);
00423 pbx_exec(chan, mixmonapp, ast_str_buffer(filename));
00424 ast_bridge_join(conference_bridge->bridge, chan, NULL, NULL, NULL);
00425
00426 ao2_lock(conference_bridge);
00427 conference_bridge->record_thread = AST_PTHREADT_NULL;
00428 ao2_unlock(conference_bridge);
00429
00430 ast_hangup(chan);
00431 ao2_ref(conference_bridge, -1);
00432 return NULL;
00433 }
00434
00435
00436
00437
00438
00439
00440
00441 static int conf_is_recording(struct conference_bridge *conference_bridge)
00442 {
00443 int res = 0;
00444 ao2_lock(conference_bridge);
00445 if (conference_bridge->record_chan || conference_bridge->record_thread != AST_PTHREADT_NULL) {
00446 res = 1;
00447 }
00448 ao2_unlock(conference_bridge);
00449 return res;
00450 }
00451
00452
00453
00454
00455
00456
00457
00458 static int conf_stop_record(struct conference_bridge *conference_bridge)
00459 {
00460 ao2_lock(conference_bridge);
00461
00462 if (conference_bridge->record_thread != AST_PTHREADT_NULL) {
00463 struct ast_channel *chan = ast_channel_ref(conference_bridge->record_chan);
00464 pthread_t thread = conference_bridge->record_thread;
00465 ao2_unlock(conference_bridge);
00466
00467 ast_bridge_remove(conference_bridge->bridge, chan);
00468 ast_queue_frame(chan, &ast_null_frame);
00469
00470 chan = ast_channel_unref(chan);
00471 pthread_join(thread, NULL);
00472 ast_test_suite_event_notify("CONF_STOP_RECORD", "Message: stopped conference recording channel\r\nConference: %s", conference_bridge->b_profile.name);
00473
00474 ao2_lock(conference_bridge);
00475 }
00476
00477
00478 if (conference_bridge->record_chan) {
00479 conference_bridge->record_chan = ast_channel_unref(conference_bridge->record_chan);
00480 }
00481
00482 ao2_unlock(conference_bridge);
00483 return 0;
00484 }
00485
00486 static int conf_start_record(struct conference_bridge *conference_bridge)
00487 {
00488 struct ast_format_cap *cap = ast_format_cap_alloc_nolock();
00489 struct ast_format tmpfmt;
00490 int cause;
00491
00492 ao2_lock(conference_bridge);
00493 if (conference_bridge->record_chan || conference_bridge->record_thread != AST_PTHREADT_NULL) {
00494 ao2_unlock(conference_bridge);
00495 return -1;
00496 }
00497 if (!cap) {
00498 ao2_unlock(conference_bridge);
00499 return -1;
00500 }
00501 if (!pbx_findapp("MixMonitor")) {
00502 ast_log(LOG_WARNING, "Can not record ConfBridge, MixMonitor app is not installed\n");
00503 cap = ast_format_cap_destroy(cap);
00504 ao2_unlock(conference_bridge);
00505 return -1;
00506 }
00507 ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
00508 if (!(conference_bridge->record_chan = ast_request("ConfBridgeRec", cap, NULL, conference_bridge->name, &cause))) {
00509 cap = ast_format_cap_destroy(cap);
00510 ao2_unlock(conference_bridge);
00511 return -1;
00512 }
00513
00514 cap = ast_format_cap_destroy(cap);
00515 ao2_ref(conference_bridge, +1);
00516
00517 if (ast_pthread_create_background(&conference_bridge->record_thread, NULL, record_thread, conference_bridge)) {
00518 ast_log(LOG_WARNING, "Failed to create recording channel for conference %s\n", conference_bridge->name);
00519
00520 ao2_unlock(conference_bridge);
00521 ao2_ref(conference_bridge, -1);
00522 return -1;
00523 }
00524
00525 ast_test_suite_event_notify("CONF_START_RECORD", "Message: started conference recording channel\r\nConference: %s", conference_bridge->b_profile.name);
00526 ao2_unlock(conference_bridge);
00527 return 0;
00528 }
00529
00530 static void send_conf_start_event(const char *conf_name)
00531 {
00532 manager_event(EVENT_FLAG_CALL, "ConfbridgeStart", "Conference: %s\r\n", conf_name);
00533 }
00534
00535 static void send_conf_end_event(const char *conf_name)
00536 {
00537 manager_event(EVENT_FLAG_CALL, "ConfbridgeEnd", "Conference: %s\r\n", conf_name);
00538 }
00539
00540 static void send_join_event(struct ast_channel *chan, const char *conf_name)
00541 {
00542 ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeJoin",
00543 "Channel: %s\r\n"
00544 "Uniqueid: %s\r\n"
00545 "Conference: %s\r\n"
00546 "CallerIDnum: %s\r\n"
00547 "CallerIDname: %s\r\n",
00548 ast_channel_name(chan),
00549 ast_channel_uniqueid(chan),
00550 conf_name,
00551 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, "<unknown>"),
00552 S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, "<unknown>")
00553 );
00554 }
00555
00556 static void send_leave_event(struct ast_channel *chan, const char *conf_name)
00557 {
00558 ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeLeave",
00559 "Channel: %s\r\n"
00560 "Uniqueid: %s\r\n"
00561 "Conference: %s\r\n"
00562 "CallerIDnum: %s\r\n"
00563 "CallerIDname: %s\r\n",
00564 ast_channel_name(chan),
00565 ast_channel_uniqueid(chan),
00566 conf_name,
00567 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, "<unknown>"),
00568 S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, "<unknown>")
00569 );
00570 }
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581 static int announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
00582 {
00583 const char *other_in_party = conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, conference_bridge->b_profile.sounds);
00584 const char *only_one = conf_get_sound(CONF_SOUND_ONLY_ONE, conference_bridge->b_profile.sounds);
00585 const char *there_are = conf_get_sound(CONF_SOUND_THERE_ARE, conference_bridge->b_profile.sounds);
00586
00587 if (conference_bridge->users == 1) {
00588
00589 return 0;
00590 } else if (conference_bridge->users == 2) {
00591 if (conference_bridge_user) {
00592
00593 if (ast_stream_and_wait(conference_bridge_user->chan,
00594 only_one,
00595 "")) {
00596 return -1;
00597 }
00598 } else {
00599 play_sound_file(conference_bridge, only_one);
00600 }
00601 } else {
00602
00603 if (conference_bridge_user) {
00604 if (ast_stream_and_wait(conference_bridge_user->chan,
00605 there_are,
00606 "")) {
00607 return -1;
00608 }
00609 if (ast_say_number(conference_bridge_user->chan, conference_bridge->users - 1, "", ast_channel_language(conference_bridge_user->chan), NULL)) {
00610 return -1;
00611 }
00612 if (ast_stream_and_wait(conference_bridge_user->chan,
00613 other_in_party,
00614 "")) {
00615 return -1;
00616 }
00617 } else {
00618 play_sound_file(conference_bridge, there_are);
00619 play_sound_number(conference_bridge, conference_bridge->users - 1);
00620 play_sound_file(conference_bridge, other_in_party);
00621 }
00622 }
00623 return 0;
00624 }
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637 static int play_prompt_to_channel(struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file)
00638 {
00639 int res;
00640 ao2_unlock(conference_bridge);
00641 res = ast_stream_and_wait(chan, file, "");
00642 ao2_lock(conference_bridge);
00643 return res;
00644 }
00645
00646 static void handle_video_on_join(struct conference_bridge *conference_bridge, struct ast_channel *chan, int marked)
00647 {
00648
00649 if (!marked) {
00650 return;
00651 }
00652
00653 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED)) {
00654 int set = 1;
00655 struct conference_bridge_user *tmp_user = NULL;
00656 ao2_lock(conference_bridge);
00657
00658 AST_LIST_TRAVERSE(&conference_bridge->users_list, tmp_user, list) {
00659 if (tmp_user->chan == chan) {
00660 continue;
00661 }
00662 if (ast_bridge_is_video_src(conference_bridge->bridge, tmp_user->chan)) {
00663 set = 0;
00664 break;
00665 }
00666 }
00667 ao2_unlock(conference_bridge);
00668 if (set) {
00669 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, chan);
00670 }
00671 } else if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
00672
00673 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, chan);
00674 }
00675 }
00676
00677 static void handle_video_on_exit(struct conference_bridge *conference_bridge, struct ast_channel *chan)
00678 {
00679 struct conference_bridge_user *tmp_user = NULL;
00680
00681
00682 if (!ast_bridge_is_video_src(conference_bridge->bridge, chan)) {
00683 return;
00684 }
00685
00686 ast_bridge_remove_video_src(conference_bridge->bridge, chan);
00687
00688
00689
00690
00691 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
00692 ast_bridge_set_talker_src_video_mode(conference_bridge->bridge);
00693 }
00694
00695
00696 if (!ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) &&
00697 !ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
00698 return;
00699 }
00700
00701
00702 ao2_lock(conference_bridge);
00703 AST_LIST_TRAVERSE(&conference_bridge->users_list, tmp_user, list) {
00704 if (tmp_user->chan == chan) {
00705 continue;
00706 }
00707 if (ast_test_flag(&tmp_user->u_profile, USER_OPT_MARKEDUSER)) {
00708 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, tmp_user->chan);
00709 break;
00710 }
00711 }
00712 ao2_unlock(conference_bridge);
00713 }
00714
00715
00716
00717
00718
00719
00720
00721
00722
00723 static int post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
00724 {
00725 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER)) {
00726 struct conference_bridge_user *other_conference_bridge_user = NULL;
00727
00728
00729
00730 if (conference_bridge->markedusers >= 2) {
00731 return 0;
00732 }
00733
00734
00735 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
00736 if (other_conference_bridge_user == conference_bridge_user) {
00737 continue;
00738 }
00739 if (other_conference_bridge_user->playing_moh && !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan)) {
00740 other_conference_bridge_user->playing_moh = 0;
00741 ast_moh_stop(other_conference_bridge_user->chan);
00742 ast_bridge_unsuspend(conference_bridge->bridge, other_conference_bridge_user->chan);
00743 }
00744 }
00745
00746
00747 if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_QUIET)) {
00748 ao2_unlock(conference_bridge);
00749 ast_autoservice_start(conference_bridge_user->chan);
00750 play_sound_file(conference_bridge,
00751 conf_get_sound(CONF_SOUND_PLACE_IN_CONF, conference_bridge_user->b_profile.sounds));
00752 ast_autoservice_stop(conference_bridge_user->chan);
00753 ao2_lock(conference_bridge);
00754 }
00755
00756
00757 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
00758 if (other_conference_bridge_user == conference_bridge_user) {
00759 continue;
00760 }
00761
00762 if (!ast_test_flag(&other_conference_bridge_user->u_profile, USER_OPT_STARTMUTED)) {
00763 other_conference_bridge_user->features.mute = 0;
00764 }
00765 }
00766 } else {
00767
00768 if (conference_bridge->markedusers) {
00769 return 0;
00770 }
00771
00772 conference_bridge_user->features.mute = 1;
00773
00774 if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_QUIET)) {
00775 if (play_prompt_to_channel(conference_bridge,
00776 conference_bridge_user->chan,
00777 conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, conference_bridge_user->b_profile.sounds))) {
00778
00779 return -1;
00780 }
00781 }
00782
00783
00784
00785
00786 if (!conference_bridge->markedusers && ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD)) {
00787 ast_moh_start(conference_bridge_user->chan, conference_bridge_user->u_profile.moh_class, NULL);
00788 conference_bridge_user->playing_moh = 1;
00789 }
00790 }
00791 return 0;
00792 }
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802 static int post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
00803 {
00804
00805 if (conference_bridge->users == 1) {
00806
00807 if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_QUIET | USER_OPT_NOONLYPERSON)) {
00808 if (play_prompt_to_channel(conference_bridge,
00809 conference_bridge_user->chan,
00810 conf_get_sound(CONF_SOUND_ONLY_PERSON, conference_bridge_user->b_profile.sounds))) {
00811
00812 return -1;
00813 }
00814 }
00815
00816
00817
00818
00819 if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD)) {
00820 ast_moh_start(conference_bridge_user->chan, conference_bridge_user->u_profile.moh_class, NULL);
00821 conference_bridge_user->playing_moh = 1;
00822 }
00823 return 0;
00824 }
00825
00826
00827 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNT)) {
00828 ao2_unlock(conference_bridge);
00829 if (announce_user_count(conference_bridge, conference_bridge_user)) {
00830 ao2_lock(conference_bridge);
00831 return -1;
00832 }
00833 ao2_lock(conference_bridge);
00834 }
00835
00836
00837 if (conference_bridge->users == 2) {
00838 struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
00839
00840
00841 if (ast_test_flag(&first_participant->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
00842 first_participant->playing_moh = 0;
00843 ast_moh_stop(first_participant->chan);
00844 ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
00845 }
00846 }
00847
00848 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNTALL) &&
00849 (conference_bridge->users > conference_bridge_user->u_profile.announce_user_count_all_after)) {
00850 ao2_unlock(conference_bridge);
00851 if (announce_user_count(conference_bridge, NULL)) {
00852 ao2_lock(conference_bridge);
00853 return -1;
00854 }
00855 ao2_lock(conference_bridge);
00856 }
00857 return 0;
00858 }
00859
00860
00861
00862
00863
00864
00865
00866
00867 static void destroy_conference_bridge(void *obj)
00868 {
00869 struct conference_bridge *conference_bridge = obj;
00870
00871 ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name);
00872
00873 ast_mutex_destroy(&conference_bridge->playback_lock);
00874
00875 if (conference_bridge->playback_chan) {
00876 struct ast_channel *underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
00877 if (underlying_channel) {
00878 ast_hangup(underlying_channel);
00879 }
00880 ast_hangup(conference_bridge->playback_chan);
00881 conference_bridge->playback_chan = NULL;
00882 }
00883
00884
00885 if (conference_bridge->bridge) {
00886 ast_bridge_destroy(conference_bridge->bridge);
00887 conference_bridge->bridge = NULL;
00888 }
00889 conf_bridge_profile_destroy(&conference_bridge->b_profile);
00890 }
00891
00892 static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user);
00893
00894
00895
00896
00897
00898
00899
00900
00901
00902 static struct conference_bridge *join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user)
00903 {
00904 struct conference_bridge *conference_bridge = NULL;
00905 struct conference_bridge tmp;
00906 int start_record = 0;
00907 int max_members_reached = 0;
00908
00909 ast_copy_string(tmp.name, name, sizeof(tmp.name));
00910
00911
00912 ao2_lock(conference_bridges);
00913
00914 ast_debug(1, "Trying to find conference bridge '%s'\n", name);
00915
00916
00917 conference_bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
00918
00919 if (conference_bridge && conference_bridge->b_profile.max_members) {
00920 max_members_reached = conference_bridge->b_profile.max_members > conference_bridge->users ? 0 : 1;
00921 }
00922
00923
00924 if (conference_bridge && (max_members_reached || conference_bridge->locked) && !ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN)) {
00925 ao2_unlock(conference_bridges);
00926 ao2_ref(conference_bridge, -1);
00927 ast_debug(1, "Conference bridge '%s' is locked and caller is not an admin\n", name);
00928 ast_stream_and_wait(conference_bridge_user->chan,
00929 conf_get_sound(CONF_SOUND_LOCKED, conference_bridge_user->b_profile.sounds),
00930 "");
00931 return NULL;
00932 }
00933
00934
00935 if (!conference_bridge) {
00936
00937 if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) {
00938 ao2_unlock(conference_bridges);
00939 ast_log(LOG_ERROR, "Conference bridge '%s' does not exist.\n", name);
00940 return NULL;
00941 }
00942
00943
00944 conference_bridge->record_thread = AST_PTHREADT_NULL;
00945 ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
00946 conf_bridge_profile_copy(&conference_bridge->b_profile, &conference_bridge_user->b_profile);
00947
00948
00949 if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_MULTIMIX, 0))) {
00950 ao2_ref(conference_bridge, -1);
00951 conference_bridge = NULL;
00952 ao2_unlock(conference_bridges);
00953 ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name);
00954 return NULL;
00955 }
00956
00957
00958 ast_bridge_set_internal_sample_rate(conference_bridge->bridge, conference_bridge->b_profile.internal_sample_rate);
00959
00960 ast_bridge_set_mixing_interval(conference_bridge->bridge, conference_bridge->b_profile.mix_interval);
00961
00962 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
00963 ast_bridge_set_talker_src_video_mode(conference_bridge->bridge);
00964 }
00965
00966
00967 ast_mutex_init(&conference_bridge->playback_lock);
00968
00969
00970 ao2_link(conference_bridges, conference_bridge);
00971
00972
00973 send_conf_start_event(conference_bridge->name);
00974 ast_debug(1, "Created conference bridge '%s' and linked to container '%p'\n", name, conference_bridges);
00975 }
00976
00977 ao2_unlock(conference_bridges);
00978
00979
00980 conference_bridge_user->conference_bridge = conference_bridge;
00981
00982 ao2_lock(conference_bridge);
00983
00984
00985 AST_LIST_INSERT_TAIL(&conference_bridge->users_list, conference_bridge_user, list);
00986
00987
00988 conference_bridge->users++;
00989
00990
00991 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER)) {
00992 conference_bridge->markedusers++;
00993 }
00994
00995
00996 if (conference_bridge->users == 1) {
00997 ast_devstate_changed(AST_DEVICE_INUSE, "confbridge:%s", conference_bridge->name);
00998 }
00999
01000
01001 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER | USER_OPT_WAITMARKED)) {
01002 if (post_join_marked(conference_bridge, conference_bridge_user)) {
01003 ao2_unlock(conference_bridge);
01004 leave_conference_bridge(conference_bridge, conference_bridge_user);
01005 return NULL;
01006 }
01007 } else {
01008 if (post_join_unmarked(conference_bridge, conference_bridge_user)) {
01009 ao2_unlock(conference_bridge);
01010 leave_conference_bridge(conference_bridge, conference_bridge_user);
01011 return NULL;
01012 }
01013 }
01014
01015
01016 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_RECORD_CONFERENCE) && !conf_is_recording(conference_bridge)) {
01017 start_record = 1;
01018 }
01019
01020 ao2_unlock(conference_bridge);
01021
01022 if (start_record) {
01023 conf_start_record(conference_bridge);
01024 }
01025
01026 return conference_bridge;
01027 }
01028
01029
01030
01031
01032
01033
01034
01035
01036 static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
01037 {
01038 ao2_lock(conference_bridge);
01039
01040
01041 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER)) {
01042 conference_bridge->markedusers--;
01043 }
01044
01045
01046 conference_bridge->users--;
01047
01048
01049 AST_LIST_REMOVE(&conference_bridge->users_list, conference_bridge_user, list);
01050
01051
01052 if (conference_bridge->users) {
01053 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER) && !conference_bridge->markedusers) {
01054 struct conference_bridge_user *other_participant = NULL;
01055
01056
01057 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
01058 other_participant->features.mute = 1;
01059 }
01060
01061
01062 if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_QUIET)) {
01063 ao2_unlock(conference_bridge);
01064 ast_autoservice_start(conference_bridge_user->chan);
01065 play_sound_file(conference_bridge,
01066 conf_get_sound(CONF_SOUND_LEADER_HAS_LEFT, conference_bridge_user->b_profile.sounds));
01067 ast_autoservice_stop(conference_bridge_user->chan);
01068 ao2_lock(conference_bridge);
01069 }
01070
01071
01072 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
01073 if (ast_test_flag(&other_participant->u_profile, USER_OPT_ENDMARKED)) {
01074 other_participant->kicked = 1;
01075 ast_bridge_remove(conference_bridge->bridge, other_participant->chan);
01076 } else if (ast_test_flag(&other_participant->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) {
01077 ast_moh_start(other_participant->chan, other_participant->u_profile.moh_class, NULL);
01078 other_participant->playing_moh = 1;
01079 ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan);
01080 }
01081 }
01082 } else if (conference_bridge->users == 1) {
01083
01084 struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
01085
01086 if (ast_test_flag(&first_participant->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
01087 ast_moh_start(first_participant->chan, first_participant->u_profile.moh_class, NULL);
01088 first_participant->playing_moh = 1;
01089 ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
01090 }
01091 }
01092 } else {
01093
01094 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "confbridge:%s", conference_bridge->name);
01095
01096 ao2_unlink(conference_bridges, conference_bridge);
01097 send_conf_end_event(conference_bridge->name);
01098 }
01099
01100
01101 ao2_unlock(conference_bridge);
01102
01103 if (!conference_bridge->users) {
01104 conf_stop_record(conference_bridge);
01105 }
01106
01107 ao2_ref(conference_bridge, -1);
01108 }
01109
01110
01111
01112
01113
01114
01115 static int alloc_playback_chan(struct conference_bridge *conference_bridge)
01116 {
01117 int cause;
01118 struct ast_format_cap *cap;
01119 struct ast_format tmpfmt;
01120
01121 if (conference_bridge->playback_chan) {
01122 return 0;
01123 }
01124 if (!(cap = ast_format_cap_alloc_nolock())) {
01125 return -1;
01126 }
01127 ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
01128 if (!(conference_bridge->playback_chan = ast_request("Bridge", cap, NULL, "", &cause))) {
01129 cap = ast_format_cap_destroy(cap);
01130 return -1;
01131 }
01132 cap = ast_format_cap_destroy(cap);
01133
01134 conference_bridge->playback_chan->bridge = conference_bridge->bridge;
01135
01136 if (ast_call(conference_bridge->playback_chan, "", 0)) {
01137 ast_hangup(conference_bridge->playback_chan);
01138 conference_bridge->playback_chan = NULL;
01139 return -1;
01140 }
01141
01142 ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name);
01143 return 0;
01144 }
01145
01146 static int play_sound_helper(struct conference_bridge *conference_bridge, const char *filename, int say_number)
01147 {
01148 struct ast_channel *underlying_channel;
01149
01150 ast_mutex_lock(&conference_bridge->playback_lock);
01151 if (!(conference_bridge->playback_chan)) {
01152 if (alloc_playback_chan(conference_bridge)) {
01153 ast_mutex_unlock(&conference_bridge->playback_lock);
01154 return -1;
01155 }
01156 underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
01157 } else {
01158
01159 underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
01160 ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL, 0);
01161 }
01162
01163
01164 if (!ast_strlen_zero(filename)) {
01165 ast_stream_and_wait(conference_bridge->playback_chan, filename, "");
01166 } else {
01167 ast_say_number(conference_bridge->playback_chan, say_number, "", ast_channel_language(conference_bridge->playback_chan), NULL);
01168 }
01169
01170 ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", ast_channel_name(underlying_channel), conference_bridge->bridge);
01171 ast_bridge_depart(conference_bridge->bridge, underlying_channel);
01172
01173 ast_mutex_unlock(&conference_bridge->playback_lock);
01174
01175 return 0;
01176 }
01177
01178
01179
01180
01181
01182
01183
01184
01185
01186
01187 static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename)
01188 {
01189 return play_sound_helper(conference_bridge, filename, 0);
01190 }
01191
01192
01193
01194
01195
01196
01197
01198
01199
01200
01201 static int play_sound_number(struct conference_bridge *conference_bridge, int say_number)
01202 {
01203 return play_sound_helper(conference_bridge, NULL, say_number);
01204 }
01205
01206 static void conf_handle_talker_destructor(void *pvt_data)
01207 {
01208 ast_free(pvt_data);
01209 }
01210
01211 static void conf_handle_talker_cb(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *pvt_data)
01212 {
01213 char *conf_name = pvt_data;
01214 int talking;
01215
01216 switch (bridge_channel->state) {
01217 case AST_BRIDGE_CHANNEL_STATE_START_TALKING:
01218 talking = 1;
01219 break;
01220 case AST_BRIDGE_CHANNEL_STATE_STOP_TALKING:
01221 talking = 0;
01222 break;
01223 default:
01224 return;
01225 }
01226
01227
01228 ast_manager_event(bridge_channel->chan, EVENT_FLAG_CALL, "ConfbridgeTalking",
01229 "Channel: %s\r\n"
01230 "Uniqueid: %s\r\n"
01231 "Conference: %s\r\n"
01232 "TalkingStatus: %s\r\n",
01233 ast_channel_name(bridge_channel->chan), ast_channel_uniqueid(bridge_channel->chan), conf_name, talking ? "on" : "off");
01234 }
01235
01236 static int conf_get_pin(struct ast_channel *chan, struct conference_bridge_user *conference_bridge_user)
01237 {
01238 char pin_guess[MAX_PIN+1] = { 0, };
01239 const char *pin = conference_bridge_user->u_profile.pin;
01240 char *tmp = pin_guess;
01241 int i, res;
01242 unsigned int len = MAX_PIN ;
01243
01244
01245 for (i = 0; i < 3; i++) {
01246 if (ast_app_getdata(chan,
01247 conf_get_sound(CONF_SOUND_GET_PIN, conference_bridge_user->b_profile.sounds),
01248 tmp, len, 0) >= 0) {
01249 if (!strcasecmp(pin, pin_guess)) {
01250 return 0;
01251 }
01252 }
01253 ast_streamfile(chan,
01254 conf_get_sound(CONF_SOUND_INVALID_PIN, conference_bridge_user->b_profile.sounds),
01255 ast_channel_language(chan));
01256 res = ast_waitstream(chan, AST_DIGIT_ANY);
01257 if (res > 0) {
01258
01259
01260 pin_guess[0] = res;
01261 pin_guess[1] = '\0';
01262 tmp = pin_guess + 1;
01263 len = MAX_PIN - 1;
01264 } else {
01265
01266 tmp = pin_guess;
01267 len = MAX_PIN;
01268 }
01269 }
01270 return -1;
01271 }
01272
01273 static int conf_rec_name(struct conference_bridge_user *user, const char *conf_name)
01274 {
01275 char destdir[PATH_MAX];
01276 int res;
01277 int duration = 20;
01278
01279 snprintf(destdir, sizeof(destdir), "%s/confbridge", ast_config_AST_SPOOL_DIR);
01280
01281 if (ast_mkdir(destdir, 0777) != 0) {
01282 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
01283 return -1;
01284 }
01285 snprintf(user->name_rec_location, sizeof(user->name_rec_location),
01286 "%s/confbridge-name-%s-%s", destdir,
01287 conf_name, ast_channel_uniqueid(user->chan));
01288
01289 res = ast_play_and_record(user->chan,
01290 "vm-rec-name",
01291 user->name_rec_location,
01292 10,
01293 "sln",
01294 &duration,
01295 NULL,
01296 ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE),
01297 0,
01298 NULL);
01299
01300 if (res == -1) {
01301 user->name_rec_location[0] = '\0';
01302 return -1;
01303 }
01304 return 0;
01305 }
01306
01307
01308 static int confbridge_exec(struct ast_channel *chan, const char *data)
01309 {
01310 int res = 0, volume_adjustments[2];
01311 int quiet = 0;
01312 char *parse;
01313 const char *b_profile_name = DEFAULT_BRIDGE_PROFILE;
01314 const char *u_profile_name = DEFAULT_USER_PROFILE;
01315 struct conference_bridge *conference_bridge = NULL;
01316 struct conference_bridge_user conference_bridge_user = {
01317 .chan = chan,
01318 .tech_args.talking_threshold = DEFAULT_TALKING_THRESHOLD,
01319 .tech_args.silence_threshold = DEFAULT_SILENCE_THRESHOLD,
01320 .tech_args.drop_silence = 0,
01321 };
01322 AST_DECLARE_APP_ARGS(args,
01323 AST_APP_ARG(conf_name);
01324 AST_APP_ARG(b_profile_name);
01325 AST_APP_ARG(u_profile_name);
01326 AST_APP_ARG(menu_name);
01327 );
01328 ast_bridge_features_init(&conference_bridge_user.features);
01329
01330 if (chan->_state != AST_STATE_UP) {
01331 ast_answer(chan);
01332 }
01333
01334 if (ast_strlen_zero(data)) {
01335 ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
01336 res = -1;
01337 goto confbridge_cleanup;
01338 }
01339
01340
01341 parse = ast_strdupa(data);
01342
01343 AST_STANDARD_APP_ARGS(args, parse);
01344
01345
01346 if (args.argc > 1 && !ast_strlen_zero(args.b_profile_name)) {
01347 b_profile_name = args.b_profile_name;
01348 }
01349 if (!conf_find_bridge_profile(chan, b_profile_name, &conference_bridge_user.b_profile)) {
01350 ast_log(LOG_WARNING, "Conference bridge profile %s does not exist\n", b_profile_name);
01351 res = -1;
01352 goto confbridge_cleanup;
01353 }
01354
01355
01356 if (args.argc > 2 && !ast_strlen_zero(args.u_profile_name)) {
01357 u_profile_name = args.u_profile_name;
01358 }
01359
01360 if (!conf_find_user_profile(chan, u_profile_name, &conference_bridge_user.u_profile)) {
01361 ast_log(LOG_WARNING, "Conference user profile %s does not exist\n", u_profile_name);
01362 res = -1;
01363 goto confbridge_cleanup;
01364 }
01365 quiet = ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_QUIET);
01366
01367
01368
01369 if (!ast_strlen_zero(conference_bridge_user.u_profile.pin)) {
01370 if (conf_get_pin(chan, &conference_bridge_user)) {
01371 res = -1;
01372 goto confbridge_cleanup;
01373 }
01374 }
01375
01376
01377 if (!quiet && ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE)) {
01378 conf_rec_name(&conference_bridge_user, args.conf_name);
01379 }
01380
01381
01382 if (args.argc > 3 && !ast_strlen_zero(args.menu_name)) {
01383 ast_copy_string(conference_bridge_user.menu_name, args.menu_name, sizeof(conference_bridge_user.menu_name));
01384 if (conf_set_menu_to_user(conference_bridge_user.menu_name, &conference_bridge_user)) {
01385 ast_log(LOG_WARNING, "Conference menu %s does not exist and can not be applied to confbridge user.\n",
01386 args.menu_name);
01387 res = -1;
01388 goto confbridge_cleanup;
01389 }
01390 }
01391
01392
01393 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DTMF_PASS)) {
01394 conference_bridge_user.features.dtmf_passthrough = 1;
01395 }
01396
01397
01398 if (conference_bridge_user.u_profile.talking_threshold) {
01399 conference_bridge_user.tech_args.talking_threshold = conference_bridge_user.u_profile.talking_threshold;
01400 }
01401 if (conference_bridge_user.u_profile.silence_threshold) {
01402 conference_bridge_user.tech_args.silence_threshold = conference_bridge_user.u_profile.silence_threshold;
01403 }
01404
01405
01406 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_TALKER_DETECT)) {
01407 char *conf_name = ast_strdup(args.conf_name);
01408 if (!(conf_name)) {
01409 res = -1;
01410 goto confbridge_cleanup;
01411 }
01412 ast_bridge_features_set_talk_detector(&conference_bridge_user.features,
01413 conf_handle_talker_cb,
01414 conf_handle_talker_destructor,
01415 conf_name);
01416 }
01417
01418
01419 if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) {
01420 res = -1;
01421 goto confbridge_cleanup;
01422 }
01423
01424
01425 volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
01426 volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
01427
01428
01429 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_STARTMUTED)) {
01430 conference_bridge_user.features.mute = 1;
01431 }
01432
01433 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DROP_SILENCE)) {
01434 conference_bridge_user.tech_args.drop_silence = 1;
01435 }
01436
01437 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_JITTERBUFFER)) {
01438 char *func_jb;
01439 if ((func_jb = ast_module_helper("", "func_jitterbuffer", 0, 0, 0, 0))) {
01440 ast_free(func_jb);
01441 ast_func_write(chan, "JITTERBUFFER(adaptive)", "default");
01442 }
01443 }
01444
01445 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DENOISE)) {
01446 char *mod_speex;
01447
01448 if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
01449 ast_free(mod_speex);
01450 ast_func_write(chan, "DENOISE(rx)", "on");
01451 }
01452 }
01453
01454
01455 if (!ast_strlen_zero(conference_bridge_user.name_rec_location)) {
01456 ast_autoservice_start(chan);
01457 play_sound_file(conference_bridge, conference_bridge_user.name_rec_location);
01458 play_sound_file(conference_bridge,
01459 conf_get_sound(CONF_SOUND_HAS_JOINED, conference_bridge_user.b_profile.sounds));
01460 ast_autoservice_stop(chan);
01461 }
01462
01463
01464 if (!quiet) {
01465 const char *join_sound = conf_get_sound(CONF_SOUND_JOIN, conference_bridge_user.b_profile.sounds);
01466 if (conference_bridge_user.playing_moh) {
01467 ast_moh_stop(chan);
01468 }
01469 ast_stream_and_wait(chan, join_sound, "");
01470 ast_autoservice_start(chan);
01471 play_sound_file(conference_bridge, join_sound);
01472 ast_autoservice_stop(chan);
01473 if (conference_bridge_user.playing_moh) {
01474 ast_moh_start(chan, conference_bridge_user.u_profile.moh_class, NULL);
01475 }
01476 }
01477
01478
01479 handle_video_on_join(conference_bridge, conference_bridge_user.chan, ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_MARKEDUSER));
01480
01481
01482 send_join_event(conference_bridge_user.chan, conference_bridge->name);
01483 ast_bridge_join(conference_bridge->bridge,
01484 chan,
01485 NULL,
01486 &conference_bridge_user.features,
01487 &conference_bridge_user.tech_args);
01488 send_leave_event(conference_bridge_user.chan, conference_bridge->name);
01489
01490
01491 if (ast_shutting_down()) {
01492 leave_conference_bridge(conference_bridge, &conference_bridge_user);
01493 conference_bridge = NULL;
01494 goto confbridge_cleanup;
01495 }
01496
01497
01498 handle_video_on_exit(conference_bridge, conference_bridge_user.chan);
01499
01500
01501 if (!quiet && !ast_strlen_zero(conference_bridge_user.name_rec_location)) {
01502 ast_autoservice_start(chan);
01503 play_sound_file(conference_bridge, conference_bridge_user.name_rec_location);
01504 play_sound_file(conference_bridge,
01505 conf_get_sound(CONF_SOUND_HAS_LEFT, conference_bridge_user.b_profile.sounds));
01506 ast_autoservice_stop(chan);
01507 }
01508
01509
01510 if (!quiet) {
01511 const char *leave_sound = conf_get_sound(CONF_SOUND_LEAVE, conference_bridge_user.b_profile.sounds);
01512 ast_autoservice_start(chan);
01513 play_sound_file(conference_bridge, leave_sound);
01514 ast_autoservice_stop(chan);
01515 }
01516
01517
01518 leave_conference_bridge(conference_bridge, &conference_bridge_user);
01519 conference_bridge = NULL;
01520
01521
01522 if (!quiet && conference_bridge_user.kicked) {
01523 res = ast_stream_and_wait(chan,
01524 conf_get_sound(CONF_SOUND_KICKED, conference_bridge_user.b_profile.sounds),
01525 "");
01526 }
01527
01528
01529 if (volume_adjustments[0]) {
01530 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
01531 }
01532 if (volume_adjustments[1]) {
01533 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
01534 }
01535
01536 if (!ast_strlen_zero(conference_bridge_user.name_rec_location)) {
01537 ast_filedelete(conference_bridge_user.name_rec_location, NULL);
01538 }
01539
01540 confbridge_cleanup:
01541 ast_bridge_features_cleanup(&conference_bridge_user.features);
01542 conf_bridge_profile_destroy(&conference_bridge_user.b_profile);
01543 return res;
01544 }
01545
01546 static int action_toggle_mute(struct conference_bridge *conference_bridge,
01547 struct conference_bridge_user *conference_bridge_user,
01548 struct ast_channel *chan)
01549 {
01550
01551 if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_WAITMARKED) || conference_bridge->markedusers) {
01552 conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0);
01553 ast_test_suite_event_notify("CONF_MUTE", "Message: participant %s %s\r\nConference: %s\r\nChannel: %s", ast_channel_name(chan), conference_bridge_user->features.mute ? "muted" : "unmuted", conference_bridge_user->b_profile.name, ast_channel_name(chan));
01554 }
01555 return ast_stream_and_wait(chan, (conference_bridge_user->features.mute ?
01556 conf_get_sound(CONF_SOUND_MUTED, conference_bridge_user->b_profile.sounds) :
01557 conf_get_sound(CONF_SOUND_UNMUTED, conference_bridge_user->b_profile.sounds)),
01558 "");
01559 }
01560
01561 static int action_toggle_mute_participants(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
01562 {
01563 struct conference_bridge_user *participant = NULL;
01564 const char *sound_to_play;
01565
01566 ao2_lock(conference_bridge);
01567
01568
01569 conference_bridge->muted = conference_bridge->muted ? 0 : 1;
01570 sound_to_play = conf_get_sound((conference_bridge->muted ? CONF_SOUND_PARTICIPANTS_MUTED : CONF_SOUND_PARTICIPANTS_UNMUTED),
01571 conference_bridge_user->b_profile.sounds);
01572
01573 AST_LIST_TRAVERSE(&conference_bridge->users_list, participant, list) {
01574 if (!ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
01575 participant->features.mute = conference_bridge->muted;
01576 }
01577 }
01578
01579 ao2_unlock(conference_bridge);
01580
01581
01582 ast_stream_and_wait(conference_bridge_user->chan, sound_to_play, "");
01583
01584
01585 ast_autoservice_start(conference_bridge_user->chan);
01586 play_sound_helper(conference_bridge, sound_to_play, 0);
01587 ast_autoservice_stop(conference_bridge_user->chan);
01588
01589 return 0;
01590 }
01591
01592 static int action_playback(struct ast_bridge_channel *bridge_channel, const char *playback_file)
01593 {
01594 char *file_copy = ast_strdupa(playback_file);
01595 char *file = NULL;
01596
01597 while ((file = strsep(&file_copy, "&"))) {
01598 if (ast_stream_and_wait(bridge_channel->chan, file, "")) {
01599 ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
01600 return -1;
01601 }
01602 }
01603 return 0;
01604 }
01605
01606 static int action_playback_and_continue(struct conference_bridge *conference_bridge,
01607 struct conference_bridge_user *conference_bridge_user,
01608 struct ast_bridge_channel *bridge_channel,
01609 struct conf_menu *menu,
01610 const char *playback_file,
01611 const char *cur_dtmf,
01612 int *stop_prompts)
01613 {
01614 int i;
01615 int digit = 0;
01616 char dtmf[MAXIMUM_DTMF_FEATURE_STRING];
01617 struct conf_menu_entry new_menu_entry = { { 0, }, };
01618 char *file_copy = ast_strdupa(playback_file);
01619 char *file = NULL;
01620
01621 while ((file = strsep(&file_copy, "&"))) {
01622 if (ast_streamfile(bridge_channel->chan, file, ast_channel_language(bridge_channel->chan))) {
01623 ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
01624 return -1;
01625 }
01626
01627
01628 if (!(digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY))) {
01629
01630 continue;
01631 } else if (digit == -1) {
01632
01633 return -1;
01634 } else {
01635 break;
01636 }
01637 }
01638 if (!digit) {
01639
01640 return -1;
01641 }
01642 ast_stopstream(bridge_channel->chan);
01643
01644
01645
01646 *stop_prompts = 1;
01647
01648
01649
01650
01651 ast_copy_string(dtmf, cur_dtmf, sizeof(dtmf));
01652 for (i = 0; i < (MAXIMUM_DTMF_FEATURE_STRING - 1); i++) {
01653 dtmf[i] = cur_dtmf[i];
01654 if (!dtmf[i]) {
01655 dtmf[i] = (char) digit;
01656 dtmf[i + 1] = '\0';
01657 i = -1;
01658 break;
01659 }
01660 }
01661
01662
01663 if (i != -1) {
01664 return 0;
01665 }
01666
01667 if (conf_find_menu_entry_by_sequence(dtmf, menu, &new_menu_entry)) {
01668 execute_menu_entry(conference_bridge,
01669 conference_bridge_user,
01670 bridge_channel,
01671 &new_menu_entry, menu);
01672 conf_menu_entry_destroy(&new_menu_entry);
01673 }
01674 return 0;
01675 }
01676
01677 static int action_kick_last(struct conference_bridge *conference_bridge,
01678 struct ast_bridge_channel *bridge_channel,
01679 struct conference_bridge_user *conference_bridge_user)
01680 {
01681 struct conference_bridge_user *last_participant = NULL;
01682 int isadmin = ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN);
01683
01684 if (!isadmin) {
01685 ast_stream_and_wait(bridge_channel->chan,
01686 conf_get_sound(CONF_SOUND_ERROR_MENU, conference_bridge_user->b_profile.sounds),
01687 "");
01688 ast_log(LOG_WARNING, "Only admin users can use the kick_last menu action. Channel %s of conf %s is not an admin.\n",
01689 ast_channel_name(bridge_channel->chan),
01690 conference_bridge->name);
01691 return -1;
01692 }
01693
01694 ao2_lock(conference_bridge);
01695 if (((last_participant = AST_LIST_LAST(&conference_bridge->users_list)) == conference_bridge_user)
01696 || (ast_test_flag(&last_participant->u_profile, USER_OPT_ADMIN))) {
01697 ao2_unlock(conference_bridge);
01698 ast_stream_and_wait(bridge_channel->chan,
01699 conf_get_sound(CONF_SOUND_ERROR_MENU, conference_bridge_user->b_profile.sounds),
01700 "");
01701 } else if (last_participant) {
01702 last_participant->kicked = 1;
01703 ast_bridge_remove(conference_bridge->bridge, last_participant->chan);
01704 ao2_unlock(conference_bridge);
01705 }
01706 return 0;
01707 }
01708
01709 static int action_dialplan_exec(struct ast_bridge_channel *bridge_channel, struct conf_menu_action *menu_action)
01710 {
01711 struct ast_pbx_args args;
01712 struct ast_pbx *pbx;
01713 char *exten;
01714 char *context;
01715 int priority;
01716 int res;
01717
01718 memset(&args, 0, sizeof(args));
01719 args.no_hangup_chan = 1;
01720
01721 ast_channel_lock(bridge_channel->chan);
01722
01723
01724 exten = ast_strdupa(bridge_channel->chan->exten);
01725 context = ast_strdupa(bridge_channel->chan->context);
01726 priority = bridge_channel->chan->priority;
01727 pbx = bridge_channel->chan->pbx;
01728 bridge_channel->chan->pbx = NULL;
01729
01730
01731 ast_copy_string(bridge_channel->chan->exten, menu_action->data.dialplan_args.exten, sizeof(bridge_channel->chan->exten));
01732 ast_copy_string(bridge_channel->chan->context, menu_action->data.dialplan_args.context, sizeof(bridge_channel->chan->context));
01733 bridge_channel->chan->priority = menu_action->data.dialplan_args.priority;
01734
01735 ast_channel_unlock(bridge_channel->chan);
01736
01737
01738 res = ast_pbx_run_args(bridge_channel->chan, &args);
01739
01740
01741 ast_channel_lock(bridge_channel->chan);
01742
01743 ast_copy_string(bridge_channel->chan->exten, exten, sizeof(bridge_channel->chan->exten));
01744 ast_copy_string(bridge_channel->chan->context, context, sizeof(bridge_channel->chan->context));
01745 bridge_channel->chan->priority = priority;
01746 bridge_channel->chan->pbx = pbx;
01747
01748 ast_channel_unlock(bridge_channel->chan);
01749
01750 return res;
01751 }
01752
01753 static int execute_menu_entry(struct conference_bridge *conference_bridge,
01754 struct conference_bridge_user *conference_bridge_user,
01755 struct ast_bridge_channel *bridge_channel,
01756 struct conf_menu_entry *menu_entry,
01757 struct conf_menu *menu)
01758 {
01759 struct conf_menu_action *menu_action;
01760 int isadmin = ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN);
01761 int stop_prompts = 0;
01762 int res = 0;
01763
01764 AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
01765 switch (menu_action->id) {
01766 case MENU_ACTION_TOGGLE_MUTE:
01767 res |= action_toggle_mute(conference_bridge,
01768 conference_bridge_user,
01769 bridge_channel->chan);
01770 break;
01771 case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
01772 if (!isadmin) {
01773 break;
01774 }
01775 action_toggle_mute_participants(conference_bridge, conference_bridge_user);
01776 break;
01777 case MENU_ACTION_PARTICIPANT_COUNT:
01778 announce_user_count(conference_bridge, conference_bridge_user);
01779 break;
01780 case MENU_ACTION_PLAYBACK:
01781 if (!stop_prompts) {
01782 res |= action_playback(bridge_channel, menu_action->data.playback_file);
01783 }
01784 break;
01785 case MENU_ACTION_RESET_LISTENING:
01786 ast_audiohook_volume_set(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 0);
01787 break;
01788 case MENU_ACTION_RESET_TALKING:
01789 ast_audiohook_volume_set(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 0);
01790 break;
01791 case MENU_ACTION_INCREASE_LISTENING:
01792 ast_audiohook_volume_adjust(conference_bridge_user->chan,
01793 AST_AUDIOHOOK_DIRECTION_WRITE, 1);
01794 break;
01795 case MENU_ACTION_DECREASE_LISTENING:
01796 ast_audiohook_volume_adjust(conference_bridge_user->chan,
01797 AST_AUDIOHOOK_DIRECTION_WRITE, -1);
01798 break;
01799 case MENU_ACTION_INCREASE_TALKING:
01800 ast_audiohook_volume_adjust(conference_bridge_user->chan,
01801 AST_AUDIOHOOK_DIRECTION_READ, 1);
01802 break;
01803 case MENU_ACTION_DECREASE_TALKING:
01804 ast_audiohook_volume_adjust(conference_bridge_user->chan,
01805 AST_AUDIOHOOK_DIRECTION_READ, -1);
01806 break;
01807 case MENU_ACTION_PLAYBACK_AND_CONTINUE:
01808 if (!(stop_prompts)) {
01809 res |= action_playback_and_continue(conference_bridge,
01810 conference_bridge_user,
01811 bridge_channel,
01812 menu,
01813 menu_action->data.playback_file,
01814 menu_entry->dtmf,
01815 &stop_prompts);
01816 }
01817 break;
01818 case MENU_ACTION_DIALPLAN_EXEC:
01819 res |= action_dialplan_exec(bridge_channel, menu_action);
01820 break;
01821 case MENU_ACTION_ADMIN_TOGGLE_LOCK:
01822 if (!isadmin) {
01823 break;
01824 }
01825 conference_bridge->locked = (!conference_bridge->locked ? 1 : 0);
01826 res |= ast_stream_and_wait(bridge_channel->chan,
01827 (conference_bridge->locked ?
01828 conf_get_sound(CONF_SOUND_LOCKED_NOW, conference_bridge_user->b_profile.sounds) :
01829 conf_get_sound(CONF_SOUND_UNLOCKED_NOW, conference_bridge_user->b_profile.sounds)),
01830 "");
01831
01832 break;
01833 case MENU_ACTION_ADMIN_KICK_LAST:
01834 res |= action_kick_last(conference_bridge, bridge_channel, conference_bridge_user);
01835 break;
01836 case MENU_ACTION_LEAVE:
01837 ao2_lock(conference_bridge);
01838 ast_bridge_remove(conference_bridge->bridge, bridge_channel->chan);
01839 ao2_unlock(conference_bridge);
01840 break;
01841 case MENU_ACTION_NOOP:
01842 break;
01843 case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
01844 ao2_lock(conference_bridge);
01845 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, bridge_channel->chan);
01846 ao2_unlock(conference_bridge);
01847 break;
01848 case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
01849 handle_video_on_exit(conference_bridge, bridge_channel->chan);
01850 break;
01851 }
01852 }
01853 return res;
01854 }
01855
01856 int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel,
01857 struct conference_bridge_user *conference_bridge_user,
01858 struct conf_menu_entry *menu_entry,
01859 struct conf_menu *menu)
01860 {
01861 struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge;
01862
01863
01864 ao2_lock(conference_bridge);
01865 if (conference_bridge_user->playing_moh) {
01866
01867 ast_moh_stop(bridge_channel->chan);
01868 }
01869 ao2_unlock(conference_bridge);
01870
01871
01872 execute_menu_entry(conference_bridge, conference_bridge_user, bridge_channel, menu_entry, menu);
01873
01874
01875 ao2_lock(conference_bridge);
01876 if (conference_bridge_user->playing_moh) {
01877 ast_moh_start(bridge_channel->chan, conference_bridge_user->u_profile.moh_class, NULL);
01878 }
01879 ao2_unlock(conference_bridge);
01880
01881 return 0;
01882 }
01883
01884 static char *complete_confbridge_name(const char *line, const char *word, int pos, int state)
01885 {
01886 int which = 0;
01887 struct conference_bridge *bridge = NULL;
01888 char *res = NULL;
01889 int wordlen = strlen(word);
01890 struct ao2_iterator i;
01891
01892 i = ao2_iterator_init(conference_bridges, 0);
01893 while ((bridge = ao2_iterator_next(&i))) {
01894 if (!strncasecmp(bridge->name, word, wordlen) && ++which > state) {
01895 res = ast_strdup(bridge->name);
01896 ao2_ref(bridge, -1);
01897 break;
01898 }
01899 ao2_ref(bridge, -1);
01900 }
01901 ao2_iterator_destroy(&i);
01902
01903 return res;
01904 }
01905
01906 static char *handle_cli_confbridge_kick(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01907 {
01908 struct conference_bridge *bridge = NULL;
01909 struct conference_bridge tmp;
01910 struct conference_bridge_user *participant = NULL;
01911
01912 switch (cmd) {
01913 case CLI_INIT:
01914 e->command = "confbridge kick";
01915 e->usage =
01916 "Usage: confbridge kick <conference> <channel>\n"
01917 " Kicks a channel out of the conference bridge.\n";
01918 return NULL;
01919 case CLI_GENERATE:
01920 if (a->pos == 2) {
01921 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
01922 }
01923
01924
01925
01926
01927
01928 return NULL;
01929 }
01930
01931 if (a->argc != 4) {
01932 return CLI_SHOWUSAGE;
01933 }
01934
01935 ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
01936 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
01937 if (!bridge) {
01938 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
01939 return CLI_SUCCESS;
01940 }
01941 ao2_lock(bridge);
01942 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
01943 if (!strncmp(a->argv[3], ast_channel_name(participant->chan), strlen(ast_channel_name(participant->chan)))) {
01944 break;
01945 }
01946 }
01947 if (participant) {
01948 ast_cli(a->fd, "Kicking %s from confbridge %s\n", ast_channel_name(participant->chan), bridge->name);
01949 participant->kicked = 1;
01950 ast_bridge_remove(bridge->bridge, participant->chan);
01951 }
01952 ao2_unlock(bridge);
01953 ao2_ref(bridge, -1);
01954 return CLI_SUCCESS;
01955 }
01956
01957 static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01958 {
01959 struct ao2_iterator i;
01960 struct conference_bridge *bridge = NULL;
01961 struct conference_bridge tmp;
01962 struct conference_bridge_user *participant = NULL;
01963
01964 switch (cmd) {
01965 case CLI_INIT:
01966 e->command = "confbridge list";
01967 e->usage =
01968 "Usage: confbridge list [<name>]\n"
01969 " Lists all currently active conference bridges.\n";
01970 return NULL;
01971 case CLI_GENERATE:
01972 if (a->pos == 2) {
01973 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
01974 }
01975 return NULL;
01976 }
01977
01978 if (a->argc == 2) {
01979 ast_cli(a->fd, "Conference Bridge Name Users Marked Locked?\n");
01980 ast_cli(a->fd, "================================ ====== ====== ========\n");
01981 i = ao2_iterator_init(conference_bridges, 0);
01982 while ((bridge = ao2_iterator_next(&i))) {
01983 ast_cli(a->fd, "%-32s %6i %6i %s\n", bridge->name, bridge->users, bridge->markedusers, (bridge->locked ? "locked" : "unlocked"));
01984 ao2_ref(bridge, -1);
01985 }
01986 ao2_iterator_destroy(&i);
01987 return CLI_SUCCESS;
01988 }
01989
01990 if (a->argc == 3) {
01991 ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
01992 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
01993 if (!bridge) {
01994 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
01995 return CLI_SUCCESS;
01996 }
01997 ast_cli(a->fd, "Channel User Profile Bridge Profile Menu CallerID\n");
01998 ast_cli(a->fd, "============================= ================ ================ ================ ================\n");
01999 ao2_lock(bridge);
02000 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
02001 ast_cli(a->fd, "%-29s ", ast_channel_name(participant->chan));
02002 ast_cli(a->fd, "%-17s", participant->u_profile.name);
02003 ast_cli(a->fd, "%-17s", participant->b_profile.name);
02004 ast_cli(a->fd, "%-17s", participant->menu_name);
02005 ast_cli(a->fd, "%-17s", S_COR(participant->chan->caller.id.number.valid, participant->chan->caller.id.number.str, "<unknown>"));
02006 ast_cli(a->fd, "\n");
02007 }
02008 ao2_unlock(bridge);
02009 ao2_ref(bridge, -1);
02010 return CLI_SUCCESS;
02011 }
02012
02013 return CLI_SHOWUSAGE;
02014 }
02015
02016
02017
02018
02019
02020
02021
02022 static int generic_lock_unlock_helper(int lock, const char *conference)
02023 {
02024 struct conference_bridge *bridge = NULL;
02025 struct conference_bridge tmp;
02026 int res = 0;
02027
02028 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
02029 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02030 if (!bridge) {
02031 return -1;
02032 }
02033 ao2_lock(bridge);
02034 bridge->locked = lock;
02035 ast_test_suite_event_notify("CONF_LOCK", "Message: conference %s\r\nConference: %s", bridge->locked ? "locked" : "unlocked", bridge->b_profile.name);
02036 ao2_unlock(bridge);
02037 ao2_ref(bridge, -1);
02038
02039 return res;
02040 }
02041
02042
02043
02044
02045
02046
02047
02048
02049 static int generic_mute_unmute_helper(int mute, const char *conference, const char *user)
02050 {
02051 struct conference_bridge *bridge = NULL;
02052 struct conference_bridge tmp;
02053 struct conference_bridge_user *participant = NULL;
02054 int res = 0;
02055 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
02056 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02057 if (!bridge) {
02058 return -1;
02059 }
02060 ao2_lock(bridge);
02061 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
02062 if (!strncmp(user, ast_channel_name(participant->chan), strlen(user))) {
02063 break;
02064 }
02065 }
02066 if (participant) {
02067 participant->features.mute = mute;
02068 ast_test_suite_event_notify("CONF_MUTE", "Message: participant %s %s\r\nConference: %s\r\nChannel: %s", ast_channel_name(participant->chan), participant->features.mute ? "muted" : "unmuted", bridge->b_profile.name, ast_channel_name(participant->chan));
02069 } else {
02070 res = -2;;
02071 }
02072 ao2_unlock(bridge);
02073 ao2_ref(bridge, -1);
02074
02075 return res;
02076 }
02077
02078 static int cli_mute_unmute_helper(int mute, struct ast_cli_args *a)
02079 {
02080 int res = generic_mute_unmute_helper(mute, a->argv[2], a->argv[3]);
02081
02082 if (res == -1) {
02083 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
02084 return -1;
02085 } else if (res == -2) {
02086 ast_cli(a->fd, "No channel named '%s' found in conference %s\n", a->argv[3], a->argv[2]);
02087 return -1;
02088 }
02089 ast_cli(a->fd, "%s %s from confbridge %s\n", mute ? "Muting" : "Unmuting", a->argv[3], a->argv[2]);
02090 return 0;
02091 }
02092
02093 static char *handle_cli_confbridge_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02094 {
02095 switch (cmd) {
02096 case CLI_INIT:
02097 e->command = "confbridge mute";
02098 e->usage =
02099 "Usage: confbridge mute <conference> <channel>\n";
02100 return NULL;
02101 case CLI_GENERATE:
02102 if (a->pos == 2) {
02103 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02104 }
02105 return NULL;
02106 }
02107 if (a->argc != 4) {
02108 return CLI_SHOWUSAGE;
02109 }
02110
02111 cli_mute_unmute_helper(1, a);
02112
02113 return CLI_SUCCESS;
02114 }
02115
02116 static char *handle_cli_confbridge_unmute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02117 {
02118 switch (cmd) {
02119 case CLI_INIT:
02120 e->command = "confbridge unmute";
02121 e->usage =
02122 "Usage: confbridge unmute <conference> <channel>\n";
02123 return NULL;
02124 case CLI_GENERATE:
02125 if (a->pos == 2) {
02126 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02127 }
02128 return NULL;
02129 }
02130 if (a->argc != 4) {
02131 return CLI_SHOWUSAGE;
02132 }
02133
02134 cli_mute_unmute_helper(0, a);
02135
02136 return CLI_SUCCESS;
02137 }
02138
02139 static char *handle_cli_confbridge_lock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02140 {
02141 switch (cmd) {
02142 case CLI_INIT:
02143 e->command = "confbridge lock";
02144 e->usage =
02145 "Usage: confbridge lock <conference>\n";
02146 return NULL;
02147 case CLI_GENERATE:
02148 if (a->pos == 2) {
02149 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02150 }
02151 return NULL;
02152 }
02153 if (a->argc != 3) {
02154 return CLI_SHOWUSAGE;
02155 }
02156 if (generic_lock_unlock_helper(1, a->argv[2])) {
02157 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
02158 } else {
02159 ast_cli(a->fd, "Conference %s is locked.\n", a->argv[2]);
02160 }
02161 return CLI_SUCCESS;
02162 }
02163
02164 static char *handle_cli_confbridge_unlock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02165 {
02166 switch (cmd) {
02167 case CLI_INIT:
02168 e->command = "confbridge unlock";
02169 e->usage =
02170 "Usage: confbridge unlock <conference>\n";
02171 return NULL;
02172 case CLI_GENERATE:
02173 if (a->pos == 2) {
02174 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02175 }
02176 return NULL;
02177 }
02178 if (a->argc != 3) {
02179 return CLI_SHOWUSAGE;
02180 }
02181 if (generic_lock_unlock_helper(0, a->argv[2])) {
02182 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
02183 } else {
02184 ast_cli(a->fd, "Conference %s is unlocked.\n", a->argv[2]);
02185 }
02186 return CLI_SUCCESS;
02187 }
02188
02189 static char *handle_cli_confbridge_start_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02190 {
02191 const char *rec_file = NULL;
02192 struct conference_bridge *bridge = NULL;
02193 struct conference_bridge tmp;
02194
02195 switch (cmd) {
02196 case CLI_INIT:
02197 e->command = "confbridge record start";
02198 e->usage =
02199 "Usage: confbridge record start <conference> <file>\n"
02200 " <file> is optional, Otherwise the bridge profile\n"
02201 " record file will be used. If the bridge profile\n"
02202 " has no record file specified, a file will automatically\n"
02203 " be generated in the monitor directory\n";
02204 return NULL;
02205 case CLI_GENERATE:
02206 if (a->pos == 3) {
02207 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02208 }
02209 return NULL;
02210 }
02211 if (a->argc < 4) {
02212 return CLI_SHOWUSAGE;
02213 }
02214 if (a->argc == 5) {
02215 rec_file = a->argv[4];
02216 }
02217
02218 ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
02219 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02220 if (!bridge) {
02221 ast_cli(a->fd, "Conference not found.\n");
02222 return CLI_FAILURE;
02223 }
02224 if (conf_is_recording(bridge)) {
02225 ast_cli(a->fd, "Conference is already being recorded.\n");
02226 ao2_ref(bridge, -1);
02227 return CLI_SUCCESS;
02228 }
02229 if (!ast_strlen_zero(rec_file)) {
02230 ao2_lock(bridge);
02231 ast_copy_string(bridge->b_profile.rec_file, rec_file, sizeof(bridge->b_profile.rec_file));
02232 ao2_unlock(bridge);
02233 }
02234 if (conf_start_record(bridge)) {
02235 ast_cli(a->fd, "Could not start recording due to internal error.\n");
02236 ao2_ref(bridge, -1);
02237 return CLI_FAILURE;
02238 }
02239 ast_cli(a->fd, "Recording started\n");
02240 ao2_ref(bridge, -1);
02241 return CLI_SUCCESS;
02242 }
02243
02244 static char *handle_cli_confbridge_stop_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02245 {
02246 struct conference_bridge *bridge = NULL;
02247 struct conference_bridge tmp;
02248
02249 switch (cmd) {
02250 case CLI_INIT:
02251 e->command = "confbridge record stop";
02252 e->usage =
02253 "Usage: confbridge record stop <conference>\n";
02254 return NULL;
02255 case CLI_GENERATE:
02256 if (a->pos == 3) {
02257 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02258 }
02259 return NULL;
02260 }
02261 if (a->argc != 4) {
02262 return CLI_SHOWUSAGE;
02263 }
02264
02265 ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
02266 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02267 if (!bridge) {
02268 ast_cli(a->fd, "Conference not found.\n");
02269 return CLI_SUCCESS;
02270 }
02271 conf_stop_record(bridge);
02272 ast_cli(a->fd, "Recording stopped.\n");
02273 ao2_ref(bridge, -1);
02274 return CLI_SUCCESS;
02275 }
02276
02277 static struct ast_cli_entry cli_confbridge[] = {
02278 AST_CLI_DEFINE(handle_cli_confbridge_list, "List conference bridges and participants."),
02279 AST_CLI_DEFINE(handle_cli_confbridge_kick, "Kick participants out of conference bridges."),
02280 AST_CLI_DEFINE(handle_cli_confbridge_mute, "Mute a participant."),
02281 AST_CLI_DEFINE(handle_cli_confbridge_unmute, "Unmute a participant."),
02282 AST_CLI_DEFINE(handle_cli_confbridge_lock, "Lock a conference."),
02283 AST_CLI_DEFINE(handle_cli_confbridge_unlock, "Unlock a conference."),
02284 AST_CLI_DEFINE(handle_cli_confbridge_start_record, "Start recording a conference"),
02285 AST_CLI_DEFINE(handle_cli_confbridge_stop_record, "Stop recording a conference."),
02286 };
02287 static struct ast_custom_function confbridge_function = {
02288 .name = "CONFBRIDGE",
02289 .write = func_confbridge_helper,
02290 };
02291
02292 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
02293 static struct ast_custom_function confbridge_info_function = {
02294 .name = "CONFBRIDGE_INFO",
02295 .read = func_confbridge_info,
02296 };
02297
02298 static int action_confbridgelist(struct mansession *s, const struct message *m)
02299 {
02300 const char *actionid = astman_get_header(m, "ActionID");
02301 const char *conference = astman_get_header(m, "Conference");
02302 struct conference_bridge_user *participant = NULL;
02303 struct conference_bridge *bridge = NULL;
02304 struct conference_bridge tmp;
02305 char id_text[80] = "";
02306 int total = 0;
02307
02308 if (!ast_strlen_zero(actionid)) {
02309 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
02310 }
02311 if (ast_strlen_zero(conference)) {
02312 astman_send_error(s, m, "No Conference name provided.");
02313 return 0;
02314 }
02315 if (!ao2_container_count(conference_bridges)) {
02316 astman_send_error(s, m, "No active conferences.");
02317 return 0;
02318 }
02319 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
02320 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02321 if (!bridge) {
02322 astman_send_error(s, m, "No Conference by that name found.");
02323 return 0;
02324 }
02325
02326 astman_send_listack(s, m, "Confbridge user list will follow", "start");
02327
02328 ao2_lock(bridge);
02329 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
02330 total++;
02331 astman_append(s,
02332 "Event: ConfbridgeList\r\n"
02333 "%s"
02334 "Conference: %s\r\n"
02335 "CallerIDNum: %s\r\n"
02336 "CallerIDName: %s\r\n"
02337 "Channel: %s\r\n"
02338 "Admin: %s\r\n"
02339 "MarkedUser: %s\r\n"
02340 "\r\n",
02341 id_text,
02342 bridge->name,
02343 S_COR(participant->chan->caller.id.number.valid, participant->chan->caller.id.number.str, "<unknown>"),
02344 S_COR(participant->chan->caller.id.name.valid, participant->chan->caller.id.name.str, "<no name>"),
02345 ast_channel_name(participant->chan),
02346 ast_test_flag(&participant->u_profile, USER_OPT_ADMIN) ? "Yes" : "No",
02347 ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER) ? "Yes" : "No");
02348 }
02349 ao2_unlock(bridge);
02350 ao2_ref(bridge, -1);
02351
02352 astman_append(s,
02353 "Event: ConfbridgeListComplete\r\n"
02354 "EventList: Complete\r\n"
02355 "ListItems: %d\r\n"
02356 "%s"
02357 "\r\n", total, id_text);
02358
02359 return 0;
02360 }
02361
02362 static int action_confbridgelistrooms(struct mansession *s, const struct message *m)
02363 {
02364 const char *actionid = astman_get_header(m, "ActionID");
02365 struct conference_bridge *bridge = NULL;
02366 struct ao2_iterator i;
02367 char id_text[512] = "";
02368 int totalitems = 0;
02369
02370 if (!ast_strlen_zero(actionid)) {
02371 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
02372 }
02373
02374 if (!ao2_container_count(conference_bridges)) {
02375 astman_send_error(s, m, "No active conferences.");
02376 return 0;
02377 }
02378
02379 astman_send_listack(s, m, "Confbridge conferences will follow", "start");
02380
02381
02382 i = ao2_iterator_init(conference_bridges, 0);
02383 while ((bridge = ao2_iterator_next(&i))) {
02384 totalitems++;
02385
02386 ao2_lock(bridge);
02387 astman_append(s,
02388 "Event: ConfbridgeListRooms\r\n"
02389 "%s"
02390 "Conference: %s\r\n"
02391 "Parties: %d\r\n"
02392 "Marked: %d\r\n"
02393 "Locked: %s\r\n"
02394 "\r\n",
02395 id_text,
02396 bridge->name,
02397 bridge->users,
02398 bridge->markedusers,
02399 bridge->locked ? "Yes" : "No");
02400 ao2_unlock(bridge);
02401
02402 ao2_ref(bridge, -1);
02403 }
02404 ao2_iterator_destroy(&i);
02405
02406
02407 astman_append(s,
02408 "Event: ConfbridgeListRoomsComplete\r\n"
02409 "EventList: Complete\r\n"
02410 "ListItems: %d\r\n"
02411 "%s"
02412 "\r\n", totalitems, id_text);
02413 return 0;
02414 }
02415
02416 static int action_mute_unmute_helper(struct mansession *s, const struct message *m, int mute)
02417 {
02418 const char *conference = astman_get_header(m, "Conference");
02419 const char *channel = astman_get_header(m, "Channel");
02420 int res = 0;
02421
02422 if (ast_strlen_zero(conference)) {
02423 astman_send_error(s, m, "No Conference name provided.");
02424 return 0;
02425 }
02426 if (ast_strlen_zero(channel)) {
02427 astman_send_error(s, m, "No channel name provided.");
02428 return 0;
02429 }
02430 if (!ao2_container_count(conference_bridges)) {
02431 astman_send_error(s, m, "No active conferences.");
02432 return 0;
02433 }
02434
02435 res = generic_mute_unmute_helper(mute, conference, channel);
02436
02437 if (res == -1) {
02438 astman_send_error(s, m, "No Conference by that name found.");
02439 return 0;
02440 } else if (res == -2) {
02441 astman_send_error(s, m, "No Channel by that name found in Conference.");
02442 return 0;
02443 }
02444
02445 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
02446 return 0;
02447 }
02448
02449 static int action_confbridgeunmute(struct mansession *s, const struct message *m)
02450 {
02451 return action_mute_unmute_helper(s, m, 0);
02452 }
02453 static int action_confbridgemute(struct mansession *s, const struct message *m)
02454 {
02455 return action_mute_unmute_helper(s, m, 1);
02456 }
02457
02458 static int action_lock_unlock_helper(struct mansession *s, const struct message *m, int lock)
02459 {
02460 const char *conference = astman_get_header(m, "Conference");
02461 int res = 0;
02462
02463 if (ast_strlen_zero(conference)) {
02464 astman_send_error(s, m, "No Conference name provided.");
02465 return 0;
02466 }
02467 if (!ao2_container_count(conference_bridges)) {
02468 astman_send_error(s, m, "No active conferences.");
02469 return 0;
02470 }
02471 if ((res = generic_lock_unlock_helper(lock, conference))) {
02472 astman_send_error(s, m, "No Conference by that name found.");
02473 return 0;
02474 }
02475 astman_send_ack(s, m, lock ? "Conference locked" : "Conference unlocked");
02476 return 0;
02477 }
02478 static int action_confbridgeunlock(struct mansession *s, const struct message *m)
02479 {
02480 return action_lock_unlock_helper(s, m, 0);
02481 }
02482 static int action_confbridgelock(struct mansession *s, const struct message *m)
02483 {
02484 return action_lock_unlock_helper(s, m, 1);
02485 }
02486
02487 static int action_confbridgekick(struct mansession *s, const struct message *m)
02488 {
02489 const char *conference = astman_get_header(m, "Conference");
02490 const char *channel = astman_get_header(m, "Channel");
02491 struct conference_bridge_user *participant = NULL;
02492 struct conference_bridge *bridge = NULL;
02493 struct conference_bridge tmp;
02494 int found = 0;
02495
02496 if (ast_strlen_zero(conference)) {
02497 astman_send_error(s, m, "No Conference name provided.");
02498 return 0;
02499 }
02500 if (!ao2_container_count(conference_bridges)) {
02501 astman_send_error(s, m, "No active conferences.");
02502 return 0;
02503 }
02504 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
02505 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02506 if (!bridge) {
02507 astman_send_error(s, m, "No Conference by that name found.");
02508 return 0;
02509 }
02510
02511 ao2_lock(bridge);
02512 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
02513 if (!strcasecmp(ast_channel_name(participant->chan), channel)) {
02514 participant->kicked = 1;
02515 ast_bridge_remove(bridge->bridge, participant->chan);
02516 found = 1;
02517 break;
02518 }
02519 }
02520 ao2_unlock(bridge);
02521 ao2_ref(bridge, -1);
02522
02523 if (found) {
02524 astman_send_ack(s, m, "User kicked");
02525 } else {
02526 astman_send_error(s, m, "No Channel by that name found in Conference.");
02527 }
02528 return 0;
02529 }
02530
02531 static int action_confbridgestartrecord(struct mansession *s, const struct message *m)
02532 {
02533 const char *conference = astman_get_header(m, "Conference");
02534 const char *recordfile = astman_get_header(m, "RecordFile");
02535 struct conference_bridge *bridge = NULL;
02536 struct conference_bridge tmp;
02537
02538 if (ast_strlen_zero(conference)) {
02539 astman_send_error(s, m, "No Conference name provided.");
02540 return 0;
02541 }
02542 if (!ao2_container_count(conference_bridges)) {
02543 astman_send_error(s, m, "No active conferences.");
02544 return 0;
02545 }
02546
02547 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
02548 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02549 if (!bridge) {
02550 astman_send_error(s, m, "No Conference by that name found.");
02551 return 0;
02552 }
02553
02554 if (conf_is_recording(bridge)) {
02555 astman_send_error(s, m, "Conference is already being recorded.");
02556 ao2_ref(bridge, -1);
02557 return 0;
02558 }
02559
02560 if (!ast_strlen_zero(recordfile)) {
02561 ao2_lock(bridge);
02562 ast_copy_string(bridge->b_profile.rec_file, recordfile, sizeof(bridge->b_profile.rec_file));
02563 ao2_unlock(bridge);
02564 }
02565
02566 if (conf_start_record(bridge)) {
02567 astman_send_error(s, m, "Internal error starting conference recording.");
02568 ao2_ref(bridge, -1);
02569 return 0;
02570 }
02571
02572 ao2_ref(bridge, -1);
02573 astman_send_ack(s, m, "Conference Recording Started.");
02574 return 0;
02575 }
02576 static int action_confbridgestoprecord(struct mansession *s, const struct message *m)
02577 {
02578 const char *conference = astman_get_header(m, "Conference");
02579 struct conference_bridge *bridge = NULL;
02580 struct conference_bridge tmp;
02581
02582 if (ast_strlen_zero(conference)) {
02583 astman_send_error(s, m, "No Conference name provided.");
02584 return 0;
02585 }
02586 if (!ao2_container_count(conference_bridges)) {
02587 astman_send_error(s, m, "No active conferences.");
02588 return 0;
02589 }
02590
02591 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
02592 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02593 if (!bridge) {
02594 astman_send_error(s, m, "No Conference by that name found.");
02595 return 0;
02596 }
02597
02598 if (conf_stop_record(bridge)) {
02599 astman_send_error(s, m, "Internal error while stopping recording.");
02600 ao2_ref(bridge, -1);
02601 return 0;
02602 }
02603
02604 ao2_ref(bridge, -1);
02605 astman_send_ack(s, m, "Conference Recording Stopped.");
02606 return 0;
02607 }
02608
02609 static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m)
02610 {
02611 const char *conference = astman_get_header(m, "Conference");
02612 const char *channel = astman_get_header(m, "Channel");
02613 struct conference_bridge_user *participant = NULL;
02614 struct conference_bridge *bridge = NULL;
02615 struct conference_bridge tmp;
02616
02617 if (ast_strlen_zero(conference)) {
02618 astman_send_error(s, m, "No Conference name provided.");
02619 return 0;
02620 }
02621 if (ast_strlen_zero(channel)) {
02622 astman_send_error(s, m, "No channel name provided.");
02623 return 0;
02624 }
02625 if (!ao2_container_count(conference_bridges)) {
02626 astman_send_error(s, m, "No active conferences.");
02627 return 0;
02628 }
02629
02630 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
02631 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02632 if (!bridge) {
02633 astman_send_error(s, m, "No Conference by that name found.");
02634 return 0;
02635 }
02636
02637
02638 ao2_lock(bridge);
02639 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
02640 if (!strncmp(channel, ast_channel_name(participant->chan), strlen(channel))) {
02641 ast_bridge_set_single_src_video_mode(bridge->bridge, participant->chan);
02642 break;
02643 }
02644 }
02645 ao2_unlock(bridge);
02646 ao2_ref(bridge, -1);
02647
02648
02649
02650 if (!participant) {
02651 astman_send_error(s, m, "No channel by that name found in conference.");
02652 return 0;
02653 }
02654 astman_send_ack(s, m, "Conference single video source set.");
02655 return 0;
02656 }
02657
02658 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
02659 {
02660 char *parse = NULL;
02661 struct conference_bridge *bridge = NULL;
02662 struct conference_bridge_user *participant = NULL;
02663 struct conference_bridge tmp;
02664 int count = 0;
02665 AST_DECLARE_APP_ARGS(args,
02666 AST_APP_ARG(type);
02667 AST_APP_ARG(confno);
02668 );
02669
02670
02671 if (ast_strlen_zero(data)) {
02672 return -1;
02673 }
02674 parse = ast_strdupa(data);
02675 AST_STANDARD_APP_ARGS(args, parse);
02676 if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
02677 return -1;
02678 }
02679 if (!ao2_container_count(conference_bridges)) {
02680 snprintf(buf, len, "0");
02681 return 0;
02682 }
02683 ast_copy_string(tmp.name, args.confno, sizeof(tmp.name));
02684 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02685 if (!bridge) {
02686 snprintf(buf, len, "0");
02687 return 0;
02688 }
02689
02690
02691 ao2_lock(bridge);
02692 if (!strncasecmp(args.type, "parties", 7)) {
02693 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
02694 count++;
02695 }
02696 } else if (!strncasecmp(args.type, "admins", 6)) {
02697 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
02698 if (ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
02699 count++;
02700 }
02701 }
02702 } else if (!strncasecmp(args.type, "marked", 6)) {
02703 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
02704 if (ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER)) {
02705 count++;
02706 }
02707 }
02708 } else if (!strncasecmp(args.type, "locked", 6)) {
02709 count = bridge->locked;
02710 } else {
02711 ast_log(LOG_ERROR, "Invalid keyword '%s' passed to CONFBRIDGE_INFO. Should be one of: "
02712 "parties, admins, marked, or locked.\n", args.type);
02713 }
02714 snprintf(buf, len, "%d", count);
02715 ao2_unlock(bridge);
02716 ao2_ref(bridge, -1);
02717 return 0;
02718 }
02719
02720
02721 static int unload_module(void)
02722 {
02723 int res = ast_unregister_application(app);
02724
02725 ast_custom_function_unregister(&confbridge_function);
02726 ast_custom_function_unregister(&confbridge_info_function);
02727
02728 ast_cli_unregister_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
02729
02730
02731 ao2_ref(conference_bridges, -1);
02732
02733 conf_destroy_config();
02734
02735 ast_channel_unregister(&record_tech);
02736 record_tech.capabilities = ast_format_cap_destroy(record_tech.capabilities);
02737
02738 res |= ast_manager_unregister("ConfbridgeList");
02739 res |= ast_manager_unregister("ConfbridgeListRooms");
02740 res |= ast_manager_unregister("ConfbridgeMute");
02741 res |= ast_manager_unregister("ConfbridgeUnmute");
02742 res |= ast_manager_unregister("ConfbridgeKick");
02743 res |= ast_manager_unregister("ConfbridgeUnlock");
02744 res |= ast_manager_unregister("ConfbridgeLock");
02745 res |= ast_manager_unregister("ConfbridgeStartRecord");
02746 res |= ast_manager_unregister("ConfbridgeStopRecord");
02747
02748 return res;
02749 }
02750
02751
02752 static int load_module(void)
02753 {
02754 int res = 0;
02755 if ((ast_custom_function_register(&confbridge_function))) {
02756 return AST_MODULE_LOAD_FAILURE;
02757 }
02758 if ((ast_custom_function_register(&confbridge_info_function))) {
02759 return AST_MODULE_LOAD_FAILURE;
02760 }
02761 if (!(record_tech.capabilities = ast_format_cap_alloc())) {
02762 return AST_MODULE_LOAD_FAILURE;
02763 }
02764 ast_format_cap_add_all(record_tech.capabilities);
02765 if (ast_channel_register(&record_tech)) {
02766 ast_log(LOG_ERROR, "Unable to register ConfBridge recorder.\n");
02767 return AST_MODULE_LOAD_FAILURE;
02768 }
02769
02770 if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
02771 return AST_MODULE_LOAD_FAILURE;
02772 }
02773 if (ast_register_application_xml(app, confbridge_exec)) {
02774 ao2_ref(conference_bridges, -1);
02775 return AST_MODULE_LOAD_FAILURE;
02776 }
02777
02778 res |= ast_cli_register_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
02779 res |= ast_manager_register_xml("ConfbridgeList", EVENT_FLAG_REPORTING, action_confbridgelist);
02780 res |= ast_manager_register_xml("ConfbridgeListRooms", EVENT_FLAG_REPORTING, action_confbridgelistrooms);
02781 res |= ast_manager_register_xml("ConfbridgeMute", EVENT_FLAG_CALL, action_confbridgemute);
02782 res |= ast_manager_register_xml("ConfbridgeUnmute", EVENT_FLAG_CALL, action_confbridgeunmute);
02783 res |= ast_manager_register_xml("ConfbridgeKick", EVENT_FLAG_CALL, action_confbridgekick);
02784 res |= ast_manager_register_xml("ConfbridgeUnlock", EVENT_FLAG_CALL, action_confbridgeunlock);
02785 res |= ast_manager_register_xml("ConfbridgeLock", EVENT_FLAG_CALL, action_confbridgelock);
02786 res |= ast_manager_register_xml("ConfbridgeStartRecord", EVENT_FLAG_CALL, action_confbridgestartrecord);
02787 res |= ast_manager_register_xml("ConfbridgeStopRecord", EVENT_FLAG_CALL, action_confbridgestoprecord);
02788 res |= ast_manager_register_xml("ConfbridgeSetSingleVideoSrc", EVENT_FLAG_CALL, action_confbridgesetsinglevideosrc);
02789
02790 conf_load_config(0);
02791 return res;
02792 }
02793
02794 static int reload(void)
02795 {
02796 return conf_load_config(1);
02797 }
02798
02799 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Conference Bridge Application",
02800 .load = load_module,
02801 .unload = unload_module,
02802 .reload = reload,
02803 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
02804 );