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