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: 415206 $")
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
01591 parse = ast_strdupa(data);
01592
01593 AST_STANDARD_APP_ARGS(args, parse);
01594
01595 if (ast_strlen_zero(args.conf_name)) {
01596 ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
01597 res = -1;
01598 goto confbridge_cleanup;
01599 }
01600
01601 if (strlen(args.conf_name) >= MAX_CONF_NAME) {
01602 ast_log(LOG_WARNING, "%s does not accept conference names longer than %d\n", app, MAX_CONF_NAME - 1);
01603 res = -1;
01604 goto confbridge_cleanup;
01605 }
01606
01607
01608 if (args.argc > 1 && !ast_strlen_zero(args.b_profile_name)) {
01609 b_profile_name = args.b_profile_name;
01610 }
01611 if (!conf_find_bridge_profile(chan, b_profile_name, &conference_bridge_user.b_profile)) {
01612 ast_log(LOG_WARNING, "Conference bridge profile %s does not exist\n", b_profile_name);
01613 res = -1;
01614 goto confbridge_cleanup;
01615 }
01616
01617
01618 if (args.argc > 2 && !ast_strlen_zero(args.u_profile_name)) {
01619 u_profile_name = args.u_profile_name;
01620 }
01621 if (!conf_find_user_profile(chan, u_profile_name, &conference_bridge_user.u_profile)) {
01622 ast_log(LOG_WARNING, "Conference user profile %s does not exist\n", u_profile_name);
01623 res = -1;
01624 goto confbridge_cleanup;
01625 }
01626
01627 quiet = ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_QUIET);
01628
01629
01630
01631 if (!ast_strlen_zero(conference_bridge_user.u_profile.pin)) {
01632 if (conf_get_pin(chan, &conference_bridge_user)) {
01633 res = -1;
01634 goto confbridge_cleanup;
01635 }
01636 }
01637
01638
01639 if (!quiet && ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE)) {
01640 conf_rec_name(&conference_bridge_user, args.conf_name);
01641 }
01642
01643
01644 if (args.argc > 3 && !ast_strlen_zero(args.menu_name)) {
01645 ast_copy_string(conference_bridge_user.menu_name, args.menu_name, sizeof(conference_bridge_user.menu_name));
01646 if (conf_set_menu_to_user(conference_bridge_user.menu_name, &conference_bridge_user)) {
01647 ast_log(LOG_WARNING, "Conference menu %s does not exist and can not be applied to confbridge user.\n",
01648 args.menu_name);
01649 res = -1;
01650 goto confbridge_cleanup;
01651 }
01652 }
01653
01654
01655 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DTMF_PASS)) {
01656 conference_bridge_user.features.dtmf_passthrough = 1;
01657 }
01658
01659
01660 if (conference_bridge_user.u_profile.talking_threshold) {
01661 conference_bridge_user.tech_args.talking_threshold = conference_bridge_user.u_profile.talking_threshold;
01662 }
01663 if (conference_bridge_user.u_profile.silence_threshold) {
01664 conference_bridge_user.tech_args.silence_threshold = conference_bridge_user.u_profile.silence_threshold;
01665 }
01666
01667
01668 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_TALKER_DETECT)) {
01669 char *conf_name = ast_strdup(args.conf_name);
01670 if (!(conf_name)) {
01671 res = -1;
01672 goto confbridge_cleanup;
01673 }
01674 ast_bridge_features_set_talk_detector(&conference_bridge_user.features,
01675 conf_handle_talker_cb,
01676 conf_handle_talker_destructor,
01677 conf_name);
01678 }
01679
01680
01681 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_STARTMUTED)) {
01682
01683 conference_bridge_user.muted = 1;
01684 }
01685
01686
01687 if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) {
01688 res = -1;
01689 goto confbridge_cleanup;
01690 }
01691
01692
01693 volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
01694 volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
01695
01696 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DROP_SILENCE)) {
01697 conference_bridge_user.tech_args.drop_silence = 1;
01698 }
01699
01700 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_JITTERBUFFER)) {
01701 char *func_jb;
01702 if ((func_jb = ast_module_helper("", "func_jitterbuffer", 0, 0, 0, 0))) {
01703 ast_free(func_jb);
01704 ast_func_write(chan, "JITTERBUFFER(adaptive)", "default");
01705 }
01706 }
01707
01708 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DENOISE)) {
01709 char *mod_speex;
01710
01711 if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
01712 ast_free(mod_speex);
01713 ast_func_write(chan, "DENOISE(rx)", "on");
01714 }
01715 }
01716
01717
01718 if (!ast_strlen_zero(conference_bridge_user.name_rec_location)) {
01719 ast_autoservice_start(chan);
01720 play_sound_file(conference_bridge, conference_bridge_user.name_rec_location);
01721 play_sound_file(conference_bridge,
01722 conf_get_sound(CONF_SOUND_HAS_JOINED, conference_bridge_user.b_profile.sounds));
01723 ast_autoservice_stop(chan);
01724 }
01725
01726
01727 if (!quiet) {
01728 const char *join_sound = conf_get_sound(CONF_SOUND_JOIN, conference_bridge_user.b_profile.sounds);
01729
01730 ast_stream_and_wait(chan, join_sound, "");
01731 ast_autoservice_start(chan);
01732 play_sound_file(conference_bridge, join_sound);
01733 ast_autoservice_stop(chan);
01734 }
01735
01736
01737 handle_video_on_join(conference_bridge, conference_bridge_user.chan, ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_MARKEDUSER));
01738
01739 conf_moh_unsuspend(&conference_bridge_user);
01740
01741
01742 send_join_event(conference_bridge_user.chan, conference_bridge->name);
01743 ast_bridge_join(conference_bridge->bridge,
01744 chan,
01745 NULL,
01746 &conference_bridge_user.features,
01747 &conference_bridge_user.tech_args);
01748 send_leave_event(conference_bridge_user.chan, conference_bridge->name);
01749
01750
01751 if (ast_shutting_down()) {
01752 leave_conference(&conference_bridge_user);
01753 conference_bridge = NULL;
01754 goto confbridge_cleanup;
01755 }
01756
01757
01758 handle_video_on_exit(conference_bridge, conference_bridge_user.chan);
01759
01760
01761 if (!quiet && !ast_strlen_zero(conference_bridge_user.name_rec_location)) {
01762 ast_autoservice_start(chan);
01763 play_sound_file(conference_bridge, conference_bridge_user.name_rec_location);
01764 play_sound_file(conference_bridge,
01765 conf_get_sound(CONF_SOUND_HAS_LEFT, conference_bridge_user.b_profile.sounds));
01766 ast_autoservice_stop(chan);
01767 }
01768
01769
01770 if (!quiet) {
01771 const char *leave_sound = conf_get_sound(CONF_SOUND_LEAVE, conference_bridge_user.b_profile.sounds);
01772 ast_autoservice_start(chan);
01773 play_sound_file(conference_bridge, leave_sound);
01774 ast_autoservice_stop(chan);
01775 }
01776
01777
01778 leave_conference(&conference_bridge_user);
01779 conference_bridge = NULL;
01780
01781
01782 if (!quiet && conference_bridge_user.kicked) {
01783 res = ast_stream_and_wait(chan,
01784 conf_get_sound(CONF_SOUND_KICKED, conference_bridge_user.b_profile.sounds),
01785 "");
01786 }
01787
01788
01789 if (volume_adjustments[0]) {
01790 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
01791 }
01792 if (volume_adjustments[1]) {
01793 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
01794 }
01795
01796 if (!ast_strlen_zero(conference_bridge_user.name_rec_location)) {
01797 ast_filedelete(conference_bridge_user.name_rec_location, NULL);
01798 }
01799
01800 confbridge_cleanup:
01801 ast_bridge_features_cleanup(&conference_bridge_user.features);
01802 conf_bridge_profile_destroy(&conference_bridge_user.b_profile);
01803 return res;
01804 }
01805
01806 static int action_toggle_mute(struct conference_bridge *conference_bridge,
01807 struct conference_bridge_user *conference_bridge_user,
01808 struct ast_channel *chan)
01809 {
01810 int mute;
01811
01812
01813 mute = !conference_bridge_user->muted;
01814 conference_bridge_user->muted = mute;
01815
01816 conf_update_user_mute(conference_bridge_user);
01817 ast_test_suite_event_notify("CONF_MUTE",
01818 "Message: participant %s %s\r\n"
01819 "Conference: %s\r\n"
01820 "Channel: %s",
01821 ast_channel_name(chan),
01822 mute ? "muted" : "unmuted",
01823 conference_bridge_user->b_profile.name,
01824 ast_channel_name(chan));
01825
01826 return ast_stream_and_wait(chan, (mute ?
01827 conf_get_sound(CONF_SOUND_MUTED, conference_bridge_user->b_profile.sounds) :
01828 conf_get_sound(CONF_SOUND_UNMUTED, conference_bridge_user->b_profile.sounds)),
01829 "");
01830 }
01831
01832 static int action_toggle_mute_participants(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
01833 {
01834 struct conference_bridge_user *participant = NULL;
01835 const char *sound_to_play;
01836 int mute;
01837
01838 ao2_lock(conference_bridge);
01839
01840
01841 mute = !conference_bridge->muted;
01842 conference_bridge->muted = mute;
01843
01844 AST_LIST_TRAVERSE(&conference_bridge->active_list, participant, list) {
01845 if (!ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
01846
01847 participant->muted = mute;
01848 conf_update_user_mute(participant);
01849 }
01850 }
01851
01852 ao2_unlock(conference_bridge);
01853
01854 sound_to_play = conf_get_sound((mute ? CONF_SOUND_PARTICIPANTS_MUTED : CONF_SOUND_PARTICIPANTS_UNMUTED),
01855 conference_bridge_user->b_profile.sounds);
01856
01857
01858 ast_stream_and_wait(conference_bridge_user->chan, sound_to_play, "");
01859
01860
01861 ast_autoservice_start(conference_bridge_user->chan);
01862 play_sound_helper(conference_bridge, sound_to_play, 0);
01863 ast_autoservice_stop(conference_bridge_user->chan);
01864
01865 return 0;
01866 }
01867
01868 static int action_playback(struct ast_bridge_channel *bridge_channel, const char *playback_file)
01869 {
01870 char *file_copy = ast_strdupa(playback_file);
01871 char *file = NULL;
01872
01873 while ((file = strsep(&file_copy, "&"))) {
01874 if (ast_stream_and_wait(bridge_channel->chan, file, "")) {
01875 ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
01876 return -1;
01877 }
01878 }
01879 return 0;
01880 }
01881
01882 static int action_playback_and_continue(struct conference_bridge *conference_bridge,
01883 struct conference_bridge_user *conference_bridge_user,
01884 struct ast_bridge_channel *bridge_channel,
01885 struct conf_menu *menu,
01886 const char *playback_file,
01887 const char *cur_dtmf,
01888 int *stop_prompts)
01889 {
01890 int i;
01891 int digit = 0;
01892 char dtmf[MAXIMUM_DTMF_FEATURE_STRING];
01893 struct conf_menu_entry new_menu_entry = { { 0, }, };
01894 char *file_copy = ast_strdupa(playback_file);
01895 char *file = NULL;
01896
01897 while ((file = strsep(&file_copy, "&"))) {
01898 if (ast_streamfile(bridge_channel->chan, file, ast_channel_language(bridge_channel->chan))) {
01899 ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
01900 return -1;
01901 }
01902
01903
01904 if (!(digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY))) {
01905
01906 continue;
01907 } else if (digit == -1) {
01908
01909 return -1;
01910 } else {
01911 break;
01912 }
01913 }
01914 if (!digit) {
01915
01916 return -1;
01917 }
01918 ast_stopstream(bridge_channel->chan);
01919
01920
01921
01922 *stop_prompts = 1;
01923
01924
01925
01926
01927 ast_copy_string(dtmf, cur_dtmf, sizeof(dtmf));
01928 for (i = 0; i < (MAXIMUM_DTMF_FEATURE_STRING - 1); i++) {
01929 dtmf[i] = cur_dtmf[i];
01930 if (!dtmf[i]) {
01931 dtmf[i] = (char) digit;
01932 dtmf[i + 1] = '\0';
01933 i = -1;
01934 break;
01935 }
01936 }
01937
01938
01939 if (i != -1) {
01940 return 0;
01941 }
01942
01943 if (conf_find_menu_entry_by_sequence(dtmf, menu, &new_menu_entry)) {
01944 execute_menu_entry(conference_bridge,
01945 conference_bridge_user,
01946 bridge_channel,
01947 &new_menu_entry, menu);
01948 conf_menu_entry_destroy(&new_menu_entry);
01949 }
01950 return 0;
01951 }
01952
01953 static int action_kick_last(struct conference_bridge *conference_bridge,
01954 struct ast_bridge_channel *bridge_channel,
01955 struct conference_bridge_user *conference_bridge_user)
01956 {
01957 struct conference_bridge_user *last_participant = NULL;
01958 int isadmin = ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN);
01959
01960 if (!isadmin) {
01961 ast_stream_and_wait(bridge_channel->chan,
01962 conf_get_sound(CONF_SOUND_ERROR_MENU, conference_bridge_user->b_profile.sounds),
01963 "");
01964 ast_log(LOG_WARNING, "Only admin users can use the kick_last menu action. Channel %s of conf %s is not an admin.\n",
01965 ast_channel_name(bridge_channel->chan),
01966 conference_bridge->name);
01967 return -1;
01968 }
01969
01970 ao2_lock(conference_bridge);
01971 if (((last_participant = AST_LIST_LAST(&conference_bridge->active_list)) == conference_bridge_user)
01972 || (ast_test_flag(&last_participant->u_profile, USER_OPT_ADMIN))) {
01973 ao2_unlock(conference_bridge);
01974 ast_stream_and_wait(bridge_channel->chan,
01975 conf_get_sound(CONF_SOUND_ERROR_MENU, conference_bridge_user->b_profile.sounds),
01976 "");
01977 } else if (last_participant && !last_participant->kicked) {
01978 last_participant->kicked = 1;
01979 ast_bridge_remove(conference_bridge->bridge, last_participant->chan);
01980 ao2_unlock(conference_bridge);
01981 }
01982 return 0;
01983 }
01984
01985 static int action_dialplan_exec(struct ast_bridge_channel *bridge_channel, struct conf_menu_action *menu_action)
01986 {
01987 struct ast_pbx_args args;
01988 struct ast_pbx *pbx;
01989 char *exten;
01990 char *context;
01991 int priority;
01992 int res;
01993
01994 memset(&args, 0, sizeof(args));
01995 args.no_hangup_chan = 1;
01996
01997 ast_channel_lock(bridge_channel->chan);
01998
01999
02000 exten = ast_strdupa(ast_channel_exten(bridge_channel->chan));
02001 context = ast_strdupa(ast_channel_context(bridge_channel->chan));
02002 priority = ast_channel_priority(bridge_channel->chan);
02003 pbx = ast_channel_pbx(bridge_channel->chan);
02004 ast_channel_pbx_set(bridge_channel->chan, NULL);
02005
02006
02007 ast_channel_exten_set(bridge_channel->chan, menu_action->data.dialplan_args.exten);
02008 ast_channel_context_set(bridge_channel->chan, menu_action->data.dialplan_args.context);
02009 ast_channel_priority_set(bridge_channel->chan, menu_action->data.dialplan_args.priority);
02010
02011 ast_channel_unlock(bridge_channel->chan);
02012
02013
02014 res = ast_pbx_run_args(bridge_channel->chan, &args);
02015
02016
02017 ast_channel_lock(bridge_channel->chan);
02018
02019 ast_channel_exten_set(bridge_channel->chan, exten);
02020 ast_channel_context_set(bridge_channel->chan, context);
02021 ast_channel_priority_set(bridge_channel->chan, priority);
02022 ast_channel_pbx_set(bridge_channel->chan, pbx);
02023
02024 ast_channel_unlock(bridge_channel->chan);
02025
02026 return res;
02027 }
02028
02029 static int execute_menu_entry(struct conference_bridge *conference_bridge,
02030 struct conference_bridge_user *conference_bridge_user,
02031 struct ast_bridge_channel *bridge_channel,
02032 struct conf_menu_entry *menu_entry,
02033 struct conf_menu *menu)
02034 {
02035 struct conf_menu_action *menu_action;
02036 int isadmin = ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN);
02037 int stop_prompts = 0;
02038 int res = 0;
02039
02040 AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
02041 switch (menu_action->id) {
02042 case MENU_ACTION_TOGGLE_MUTE:
02043 res |= action_toggle_mute(conference_bridge,
02044 conference_bridge_user,
02045 bridge_channel->chan);
02046 break;
02047 case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
02048 if (!isadmin) {
02049 break;
02050 }
02051 action_toggle_mute_participants(conference_bridge, conference_bridge_user);
02052 break;
02053 case MENU_ACTION_PARTICIPANT_COUNT:
02054 announce_user_count(conference_bridge, conference_bridge_user);
02055 break;
02056 case MENU_ACTION_PLAYBACK:
02057 if (!stop_prompts) {
02058 res |= action_playback(bridge_channel, menu_action->data.playback_file);
02059 }
02060 break;
02061 case MENU_ACTION_RESET_LISTENING:
02062 ast_audiohook_volume_set(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 0);
02063 break;
02064 case MENU_ACTION_RESET_TALKING:
02065 ast_audiohook_volume_set(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 0);
02066 break;
02067 case MENU_ACTION_INCREASE_LISTENING:
02068 ast_audiohook_volume_adjust(conference_bridge_user->chan,
02069 AST_AUDIOHOOK_DIRECTION_WRITE, 1);
02070 break;
02071 case MENU_ACTION_DECREASE_LISTENING:
02072 ast_audiohook_volume_adjust(conference_bridge_user->chan,
02073 AST_AUDIOHOOK_DIRECTION_WRITE, -1);
02074 break;
02075 case MENU_ACTION_INCREASE_TALKING:
02076 ast_audiohook_volume_adjust(conference_bridge_user->chan,
02077 AST_AUDIOHOOK_DIRECTION_READ, 1);
02078 break;
02079 case MENU_ACTION_DECREASE_TALKING:
02080 ast_audiohook_volume_adjust(conference_bridge_user->chan,
02081 AST_AUDIOHOOK_DIRECTION_READ, -1);
02082 break;
02083 case MENU_ACTION_PLAYBACK_AND_CONTINUE:
02084 if (!(stop_prompts)) {
02085 res |= action_playback_and_continue(conference_bridge,
02086 conference_bridge_user,
02087 bridge_channel,
02088 menu,
02089 menu_action->data.playback_file,
02090 menu_entry->dtmf,
02091 &stop_prompts);
02092 }
02093 break;
02094 case MENU_ACTION_DIALPLAN_EXEC:
02095 res |= action_dialplan_exec(bridge_channel, menu_action);
02096 break;
02097 case MENU_ACTION_ADMIN_TOGGLE_LOCK:
02098 if (!isadmin) {
02099 break;
02100 }
02101 conference_bridge->locked = (!conference_bridge->locked ? 1 : 0);
02102 res |= ast_stream_and_wait(bridge_channel->chan,
02103 (conference_bridge->locked ?
02104 conf_get_sound(CONF_SOUND_LOCKED_NOW, conference_bridge_user->b_profile.sounds) :
02105 conf_get_sound(CONF_SOUND_UNLOCKED_NOW, conference_bridge_user->b_profile.sounds)),
02106 "");
02107
02108 break;
02109 case MENU_ACTION_ADMIN_KICK_LAST:
02110 res |= action_kick_last(conference_bridge, bridge_channel, conference_bridge_user);
02111 break;
02112 case MENU_ACTION_LEAVE:
02113 ao2_lock(conference_bridge);
02114 ast_bridge_remove(conference_bridge->bridge, bridge_channel->chan);
02115 ao2_unlock(conference_bridge);
02116 break;
02117 case MENU_ACTION_NOOP:
02118 break;
02119 case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
02120 ao2_lock(conference_bridge);
02121 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, bridge_channel->chan);
02122 ao2_unlock(conference_bridge);
02123 break;
02124 case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
02125 handle_video_on_exit(conference_bridge, bridge_channel->chan);
02126 break;
02127 }
02128 }
02129 return res;
02130 }
02131
02132 int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel,
02133 struct conference_bridge_user *conference_bridge_user,
02134 struct conf_menu_entry *menu_entry,
02135 struct conf_menu *menu)
02136 {
02137
02138 conf_moh_suspend(conference_bridge_user);
02139
02140
02141 execute_menu_entry(conference_bridge_user->conference_bridge, conference_bridge_user, bridge_channel, menu_entry, menu);
02142
02143
02144 conf_moh_unsuspend(conference_bridge_user);
02145
02146 return 0;
02147 }
02148
02149 static int kick_conference_participant(struct conference_bridge *bridge, const char *channel)
02150 {
02151 struct conference_bridge_user *participant = NULL;
02152
02153 ao2_lock(bridge);
02154 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
02155 if (!strcasecmp(ast_channel_name(participant->chan), channel) && !participant->kicked) {
02156 participant->kicked = 1;
02157 ast_bridge_remove(bridge->bridge, participant->chan);
02158 ao2_unlock(bridge);
02159 return 0;
02160 }
02161 }
02162 AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) {
02163 if (!strcasecmp(ast_channel_name(participant->chan), channel) && !participant->kicked) {
02164 participant->kicked = 1;
02165 ast_bridge_remove(bridge->bridge, participant->chan);
02166 ao2_unlock(bridge);
02167 return 0;
02168 }
02169 }
02170 ao2_unlock(bridge);
02171
02172 return -1;
02173 }
02174
02175 static char *complete_confbridge_name(const char *line, const char *word, int pos, int state)
02176 {
02177 int which = 0;
02178 struct conference_bridge *bridge = NULL;
02179 char *res = NULL;
02180 int wordlen = strlen(word);
02181 struct ao2_iterator i;
02182
02183 i = ao2_iterator_init(conference_bridges, 0);
02184 while ((bridge = ao2_iterator_next(&i))) {
02185 if (!strncasecmp(bridge->name, word, wordlen) && ++which > state) {
02186 res = ast_strdup(bridge->name);
02187 ao2_ref(bridge, -1);
02188 break;
02189 }
02190 ao2_ref(bridge, -1);
02191 }
02192 ao2_iterator_destroy(&i);
02193
02194 return res;
02195 }
02196
02197 static char *complete_confbridge_participant(const char *bridge_name, const char *line, const char *word, int pos, int state)
02198 {
02199 int which = 0;
02200 RAII_VAR(struct conference_bridge *, bridge, NULL, ao2_cleanup);
02201 struct conference_bridge tmp;
02202 struct conference_bridge_user *participant;
02203 char *res = NULL;
02204 int wordlen = strlen(word);
02205
02206 ast_copy_string(tmp.name, bridge_name, sizeof(tmp.name));
02207 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02208 if (!bridge) {
02209 return NULL;
02210 }
02211
02212 ao2_lock(bridge);
02213 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
02214 if (!strncasecmp(ast_channel_name(participant->chan), word, wordlen) && ++which > state) {
02215 res = ast_strdup(ast_channel_name(participant->chan));
02216 ao2_unlock(bridge);
02217 return res;
02218 }
02219 }
02220
02221 AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) {
02222 if (!strncasecmp(ast_channel_name(participant->chan), word, wordlen) && ++which > state) {
02223 res = ast_strdup(ast_channel_name(participant->chan));
02224 ao2_unlock(bridge);
02225 return res;
02226 }
02227 }
02228 ao2_unlock(bridge);
02229
02230 return NULL;
02231 }
02232
02233 static char *handle_cli_confbridge_kick(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02234 {
02235 struct conference_bridge *bridge = NULL;
02236 struct conference_bridge tmp;
02237 int not_found;
02238
02239 switch (cmd) {
02240 case CLI_INIT:
02241 e->command = "confbridge kick";
02242 e->usage =
02243 "Usage: confbridge kick <conference> <channel>\n"
02244 " Kicks a channel out of the conference bridge.\n";
02245 return NULL;
02246 case CLI_GENERATE:
02247 if (a->pos == 2) {
02248 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02249 }
02250 if (a->pos == 3) {
02251 return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
02252 }
02253 return NULL;
02254 }
02255
02256 if (a->argc != 4) {
02257 return CLI_SHOWUSAGE;
02258 }
02259
02260 ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
02261 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02262 if (!bridge) {
02263 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
02264 return CLI_SUCCESS;
02265 }
02266 not_found = kick_conference_participant(bridge, a->argv[3]);
02267 ao2_ref(bridge, -1);
02268 if (not_found) {
02269 ast_cli(a->fd, "No participant named '%s' found!\n", a->argv[3]);
02270 return CLI_SUCCESS;
02271 }
02272 ast_cli(a->fd, "Participant '%s' kicked out of conference '%s'\n", a->argv[3], a->argv[2]);
02273 return CLI_SUCCESS;
02274 }
02275
02276 static void handle_cli_confbridge_list_item(struct ast_cli_args *a, struct conference_bridge_user *participant)
02277 {
02278 ast_cli(a->fd, "%-30s %-16s %-16s %-16s %-16s %s\n",
02279 ast_channel_name(participant->chan),
02280 participant->u_profile.name,
02281 participant->b_profile.name,
02282 participant->menu_name,
02283 S_COR(ast_channel_caller(participant->chan)->id.number.valid,
02284 ast_channel_caller(participant->chan)->id.number.str, "<unknown>"),
02285 AST_CLI_YESNO(participant->muted));
02286 }
02287
02288 static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02289 {
02290 struct ao2_iterator i;
02291 struct conference_bridge *bridge = NULL;
02292 struct conference_bridge tmp;
02293 struct conference_bridge_user *participant = NULL;
02294
02295 switch (cmd) {
02296 case CLI_INIT:
02297 e->command = "confbridge list";
02298 e->usage =
02299 "Usage: confbridge list [<name>]\n"
02300 " Lists all currently active conference bridges.\n";
02301 return NULL;
02302 case CLI_GENERATE:
02303 if (a->pos == 2) {
02304 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02305 }
02306 return NULL;
02307 }
02308
02309 if (a->argc == 2) {
02310 ast_cli(a->fd, "Conference Bridge Name Users Marked Locked?\n");
02311 ast_cli(a->fd, "================================ ====== ====== ========\n");
02312 i = ao2_iterator_init(conference_bridges, 0);
02313 while ((bridge = ao2_iterator_next(&i))) {
02314 ast_cli(a->fd, "%-32s %6u %6u %s\n", bridge->name, bridge->activeusers + bridge->waitingusers, bridge->markedusers, (bridge->locked ? "locked" : "unlocked"));
02315 ao2_ref(bridge, -1);
02316 }
02317 ao2_iterator_destroy(&i);
02318 return CLI_SUCCESS;
02319 }
02320
02321 if (a->argc == 3) {
02322 ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
02323 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02324 if (!bridge) {
02325 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
02326 return CLI_SUCCESS;
02327 }
02328 ast_cli(a->fd, "Channel User Profile Bridge Profile Menu CallerID Muted\n");
02329 ast_cli(a->fd, "============================== ================ ================ ================ ================ =====\n");
02330 ao2_lock(bridge);
02331 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
02332 handle_cli_confbridge_list_item(a, participant);
02333 }
02334 AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) {
02335 handle_cli_confbridge_list_item(a, participant);
02336 }
02337 ao2_unlock(bridge);
02338 ao2_ref(bridge, -1);
02339 return CLI_SUCCESS;
02340 }
02341
02342 return CLI_SHOWUSAGE;
02343 }
02344
02345
02346
02347
02348
02349
02350
02351 static int generic_lock_unlock_helper(int lock, const char *conference)
02352 {
02353 struct conference_bridge *bridge = NULL;
02354 struct conference_bridge tmp;
02355 int res = 0;
02356
02357 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
02358 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02359 if (!bridge) {
02360 return -1;
02361 }
02362 ao2_lock(bridge);
02363 bridge->locked = lock;
02364 ast_test_suite_event_notify("CONF_LOCK", "Message: conference %s\r\nConference: %s", bridge->locked ? "locked" : "unlocked", bridge->b_profile.name);
02365 ao2_unlock(bridge);
02366 ao2_ref(bridge, -1);
02367
02368 return res;
02369 }
02370
02371
02372
02373
02374
02375
02376
02377
02378 static int generic_mute_unmute_helper(int mute, const char *conference, const char *user)
02379 {
02380 struct conference_bridge *bridge = NULL;
02381 struct conference_bridge tmp;
02382 struct conference_bridge_user *participant = NULL;
02383 int res = 0;
02384 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
02385 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02386 if (!bridge) {
02387 return -1;
02388 }
02389 ao2_lock(bridge);
02390 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
02391 if (!strncmp(user, ast_channel_name(participant->chan), strlen(user))) {
02392 break;
02393 }
02394 }
02395 if (!participant) {
02396
02397 AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) {
02398 if (!strncmp(user, ast_channel_name(participant->chan), strlen(user))) {
02399 break;
02400 }
02401 }
02402 }
02403 if (participant) {
02404
02405 participant->muted = mute ? 1 : 0;
02406
02407 conf_update_user_mute(participant);
02408 ast_test_suite_event_notify("CONF_MUTE",
02409 "Message: participant %s %s\r\n"
02410 "Conference: %s\r\n"
02411 "Channel: %s",
02412 ast_channel_name(participant->chan),
02413 mute ? "muted" : "unmuted",
02414 bridge->b_profile.name,
02415 ast_channel_name(participant->chan));
02416 } else {
02417 res = -2;;
02418 }
02419 ao2_unlock(bridge);
02420 ao2_ref(bridge, -1);
02421
02422 return res;
02423 }
02424
02425 static int cli_mute_unmute_helper(int mute, struct ast_cli_args *a)
02426 {
02427 int res = generic_mute_unmute_helper(mute, a->argv[2], a->argv[3]);
02428
02429 if (res == -1) {
02430 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
02431 return -1;
02432 } else if (res == -2) {
02433 ast_cli(a->fd, "No channel named '%s' found in conference %s\n", a->argv[3], a->argv[2]);
02434 return -1;
02435 }
02436 ast_cli(a->fd, "%s %s from confbridge %s\n", mute ? "Muting" : "Unmuting", a->argv[3], a->argv[2]);
02437 return 0;
02438 }
02439
02440 static char *handle_cli_confbridge_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02441 {
02442 switch (cmd) {
02443 case CLI_INIT:
02444 e->command = "confbridge mute";
02445 e->usage =
02446 "Usage: confbridge mute <conference> <channel>\n"
02447 " Mute a channel in a conference.\n"
02448 " If the specified channel is a prefix,\n"
02449 " the action will be taken on the first\n"
02450 " matching channel.\n";
02451 return NULL;
02452 case CLI_GENERATE:
02453 if (a->pos == 2) {
02454 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02455 }
02456 if (a->pos == 3) {
02457 return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
02458 }
02459 return NULL;
02460 }
02461 if (a->argc != 4) {
02462 return CLI_SHOWUSAGE;
02463 }
02464
02465 cli_mute_unmute_helper(1, a);
02466
02467 return CLI_SUCCESS;
02468 }
02469
02470 static char *handle_cli_confbridge_unmute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02471 {
02472 switch (cmd) {
02473 case CLI_INIT:
02474 e->command = "confbridge unmute";
02475 e->usage =
02476 "Usage: confbridge unmute <conference> <channel>\n"
02477 " Unmute a channel in a conference.\n"
02478 " If the specified channel is a prefix,\n"
02479 " the action will be taken on the first\n"
02480 " matching channel.\n";
02481 return NULL;
02482 case CLI_GENERATE:
02483 if (a->pos == 2) {
02484 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02485 }
02486 if (a->pos == 3) {
02487 return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
02488 }
02489 return NULL;
02490 }
02491 if (a->argc != 4) {
02492 return CLI_SHOWUSAGE;
02493 }
02494
02495 cli_mute_unmute_helper(0, a);
02496
02497 return CLI_SUCCESS;
02498 }
02499
02500 static char *handle_cli_confbridge_lock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02501 {
02502 switch (cmd) {
02503 case CLI_INIT:
02504 e->command = "confbridge lock";
02505 e->usage =
02506 "Usage: confbridge lock <conference>\n"
02507 " Lock a conference. While locked, no new non-admins\n"
02508 " may join the conference.\n";
02509 return NULL;
02510 case CLI_GENERATE:
02511 if (a->pos == 2) {
02512 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02513 }
02514 return NULL;
02515 }
02516 if (a->argc != 3) {
02517 return CLI_SHOWUSAGE;
02518 }
02519 if (generic_lock_unlock_helper(1, a->argv[2])) {
02520 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
02521 } else {
02522 ast_cli(a->fd, "Conference %s is locked.\n", a->argv[2]);
02523 }
02524 return CLI_SUCCESS;
02525 }
02526
02527 static char *handle_cli_confbridge_unlock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02528 {
02529 switch (cmd) {
02530 case CLI_INIT:
02531 e->command = "confbridge unlock";
02532 e->usage =
02533 "Usage: confbridge unlock <conference>\n"
02534 " Unlock a previously locked conference.\n";
02535 return NULL;
02536 case CLI_GENERATE:
02537 if (a->pos == 2) {
02538 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02539 }
02540 return NULL;
02541 }
02542 if (a->argc != 3) {
02543 return CLI_SHOWUSAGE;
02544 }
02545 if (generic_lock_unlock_helper(0, a->argv[2])) {
02546 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
02547 } else {
02548 ast_cli(a->fd, "Conference %s is unlocked.\n", a->argv[2]);
02549 }
02550 return CLI_SUCCESS;
02551 }
02552
02553 static char *handle_cli_confbridge_start_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02554 {
02555 const char *rec_file = NULL;
02556 struct conference_bridge *bridge = NULL;
02557 struct conference_bridge tmp;
02558
02559 switch (cmd) {
02560 case CLI_INIT:
02561 e->command = "confbridge record start";
02562 e->usage =
02563 "Usage: confbridge record start <conference> <file>\n"
02564 " <file> is optional, Otherwise the bridge profile\n"
02565 " record file will be used. If the bridge profile\n"
02566 " has no record file specified, a file will automatically\n"
02567 " be generated in the monitor directory\n";
02568 return NULL;
02569 case CLI_GENERATE:
02570 if (a->pos == 3) {
02571 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02572 }
02573 return NULL;
02574 }
02575 if (a->argc < 4) {
02576 return CLI_SHOWUSAGE;
02577 }
02578 if (a->argc == 5) {
02579 rec_file = a->argv[4];
02580 }
02581
02582 ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
02583 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02584 if (!bridge) {
02585 ast_cli(a->fd, "Conference not found.\n");
02586 return CLI_FAILURE;
02587 }
02588 ao2_lock(bridge);
02589 if (conf_is_recording(bridge)) {
02590 ast_cli(a->fd, "Conference is already being recorded.\n");
02591 ao2_unlock(bridge);
02592 ao2_ref(bridge, -1);
02593 return CLI_SUCCESS;
02594 }
02595 if (!ast_strlen_zero(rec_file)) {
02596 ast_copy_string(bridge->b_profile.rec_file, rec_file, sizeof(bridge->b_profile.rec_file));
02597 }
02598
02599 if (start_conf_record_thread(bridge)) {
02600 ast_cli(a->fd, "Could not start recording due to internal error.\n");
02601 ao2_unlock(bridge);
02602 ao2_ref(bridge, -1);
02603 return CLI_FAILURE;
02604 }
02605 ao2_unlock(bridge);
02606
02607 ast_cli(a->fd, "Recording started\n");
02608 ao2_ref(bridge, -1);
02609 return CLI_SUCCESS;
02610 }
02611
02612 static char *handle_cli_confbridge_stop_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02613 {
02614 struct conference_bridge *bridge = NULL;
02615 struct conference_bridge tmp;
02616 int ret;
02617
02618 switch (cmd) {
02619 case CLI_INIT:
02620 e->command = "confbridge record stop";
02621 e->usage =
02622 "Usage: confbridge record stop <conference>\n"
02623 " Stop a previously started recording.\n";
02624 return NULL;
02625 case CLI_GENERATE:
02626 if (a->pos == 3) {
02627 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
02628 }
02629 return NULL;
02630 }
02631 if (a->argc != 4) {
02632 return CLI_SHOWUSAGE;
02633 }
02634
02635 ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
02636 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02637 if (!bridge) {
02638 ast_cli(a->fd, "Conference not found.\n");
02639 return CLI_SUCCESS;
02640 }
02641 ao2_lock(bridge);
02642 ret = conf_stop_record(bridge);
02643 ao2_unlock(bridge);
02644 ast_cli(a->fd, "Recording %sstopped.\n", ret ? "could not be " : "");
02645 ao2_ref(bridge, -1);
02646 return CLI_SUCCESS;
02647 }
02648
02649 static struct ast_cli_entry cli_confbridge[] = {
02650 AST_CLI_DEFINE(handle_cli_confbridge_list, "List conference bridges and participants."),
02651 AST_CLI_DEFINE(handle_cli_confbridge_kick, "Kick participants out of conference bridges."),
02652 AST_CLI_DEFINE(handle_cli_confbridge_mute, "Mute a participant."),
02653 AST_CLI_DEFINE(handle_cli_confbridge_unmute, "Unmute a participant."),
02654 AST_CLI_DEFINE(handle_cli_confbridge_lock, "Lock a conference."),
02655 AST_CLI_DEFINE(handle_cli_confbridge_unlock, "Unlock a conference."),
02656 AST_CLI_DEFINE(handle_cli_confbridge_start_record, "Start recording a conference"),
02657 AST_CLI_DEFINE(handle_cli_confbridge_stop_record, "Stop recording a conference."),
02658 };
02659 static struct ast_custom_function confbridge_function = {
02660 .name = "CONFBRIDGE",
02661 .write = func_confbridge_helper,
02662 };
02663
02664 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
02665 static struct ast_custom_function confbridge_info_function = {
02666 .name = "CONFBRIDGE_INFO",
02667 .read = func_confbridge_info,
02668 };
02669
02670 static void action_confbridgelist_item(struct mansession *s, const char *id_text, struct conference_bridge *bridge, struct conference_bridge_user *participant)
02671 {
02672 astman_append(s,
02673 "Event: ConfbridgeList\r\n"
02674 "%s"
02675 "Conference: %s\r\n"
02676 "CallerIDNum: %s\r\n"
02677 "CallerIDName: %s\r\n"
02678 "Channel: %s\r\n"
02679 "Admin: %s\r\n"
02680 "MarkedUser: %s\r\n"
02681 "Muted: %s\r\n"
02682 "\r\n",
02683 id_text,
02684 bridge->name,
02685 S_COR(ast_channel_caller(participant->chan)->id.number.valid, ast_channel_caller(participant->chan)->id.number.str, "<unknown>"),
02686 S_COR(ast_channel_caller(participant->chan)->id.name.valid, ast_channel_caller(participant->chan)->id.name.str, "<no name>"),
02687 ast_channel_name(participant->chan),
02688 ast_test_flag(&participant->u_profile, USER_OPT_ADMIN) ? "Yes" : "No",
02689 ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER) ? "Yes" : "No",
02690 participant->muted ? "Yes" : "No");
02691 }
02692
02693 static int action_confbridgelist(struct mansession *s, const struct message *m)
02694 {
02695 const char *actionid = astman_get_header(m, "ActionID");
02696 const char *conference = astman_get_header(m, "Conference");
02697 struct conference_bridge_user *participant = NULL;
02698 struct conference_bridge *bridge = NULL;
02699 struct conference_bridge tmp;
02700 char id_text[80] = "";
02701 int total = 0;
02702
02703 if (!ast_strlen_zero(actionid)) {
02704 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
02705 }
02706 if (ast_strlen_zero(conference)) {
02707 astman_send_error(s, m, "No Conference name provided.");
02708 return 0;
02709 }
02710 if (!ao2_container_count(conference_bridges)) {
02711 astman_send_error(s, m, "No active conferences.");
02712 return 0;
02713 }
02714 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
02715 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02716 if (!bridge) {
02717 astman_send_error(s, m, "No Conference by that name found.");
02718 return 0;
02719 }
02720
02721 astman_send_listack(s, m, "Confbridge user list will follow", "start");
02722
02723 ao2_lock(bridge);
02724 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
02725 total++;
02726 action_confbridgelist_item(s, id_text, bridge, participant);
02727 }
02728 AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) {
02729 total++;
02730 action_confbridgelist_item(s, id_text, bridge, participant);
02731 }
02732 ao2_unlock(bridge);
02733 ao2_ref(bridge, -1);
02734
02735 astman_append(s,
02736 "Event: ConfbridgeListComplete\r\n"
02737 "EventList: Complete\r\n"
02738 "ListItems: %d\r\n"
02739 "%s"
02740 "\r\n", total, id_text);
02741
02742 return 0;
02743 }
02744
02745 static int action_confbridgelistrooms(struct mansession *s, const struct message *m)
02746 {
02747 const char *actionid = astman_get_header(m, "ActionID");
02748 struct conference_bridge *bridge = NULL;
02749 struct ao2_iterator i;
02750 char id_text[512] = "";
02751 int totalitems = 0;
02752
02753 if (!ast_strlen_zero(actionid)) {
02754 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
02755 }
02756
02757 if (!ao2_container_count(conference_bridges)) {
02758 astman_send_error(s, m, "No active conferences.");
02759 return 0;
02760 }
02761
02762 astman_send_listack(s, m, "Confbridge conferences will follow", "start");
02763
02764
02765 i = ao2_iterator_init(conference_bridges, 0);
02766 while ((bridge = ao2_iterator_next(&i))) {
02767 totalitems++;
02768
02769 ao2_lock(bridge);
02770 astman_append(s,
02771 "Event: ConfbridgeListRooms\r\n"
02772 "%s"
02773 "Conference: %s\r\n"
02774 "Parties: %u\r\n"
02775 "Marked: %u\r\n"
02776 "Locked: %s\r\n"
02777 "\r\n",
02778 id_text,
02779 bridge->name,
02780 bridge->activeusers + bridge->waitingusers,
02781 bridge->markedusers,
02782 bridge->locked ? "Yes" : "No");
02783 ao2_unlock(bridge);
02784
02785 ao2_ref(bridge, -1);
02786 }
02787 ao2_iterator_destroy(&i);
02788
02789
02790 astman_append(s,
02791 "Event: ConfbridgeListRoomsComplete\r\n"
02792 "EventList: Complete\r\n"
02793 "ListItems: %d\r\n"
02794 "%s"
02795 "\r\n", totalitems, id_text);
02796 return 0;
02797 }
02798
02799 static int action_mute_unmute_helper(struct mansession *s, const struct message *m, int mute)
02800 {
02801 const char *conference = astman_get_header(m, "Conference");
02802 const char *channel = astman_get_header(m, "Channel");
02803 int res = 0;
02804
02805 if (ast_strlen_zero(conference)) {
02806 astman_send_error(s, m, "No Conference name provided.");
02807 return 0;
02808 }
02809 if (ast_strlen_zero(channel)) {
02810 astman_send_error(s, m, "No channel name provided.");
02811 return 0;
02812 }
02813 if (!ao2_container_count(conference_bridges)) {
02814 astman_send_error(s, m, "No active conferences.");
02815 return 0;
02816 }
02817
02818 res = generic_mute_unmute_helper(mute, conference, channel);
02819
02820 if (res == -1) {
02821 astman_send_error(s, m, "No Conference by that name found.");
02822 return 0;
02823 } else if (res == -2) {
02824 astman_send_error(s, m, "No Channel by that name found in Conference.");
02825 return 0;
02826 }
02827
02828 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
02829 return 0;
02830 }
02831
02832 static int action_confbridgeunmute(struct mansession *s, const struct message *m)
02833 {
02834 return action_mute_unmute_helper(s, m, 0);
02835 }
02836 static int action_confbridgemute(struct mansession *s, const struct message *m)
02837 {
02838 return action_mute_unmute_helper(s, m, 1);
02839 }
02840
02841 static int action_lock_unlock_helper(struct mansession *s, const struct message *m, int lock)
02842 {
02843 const char *conference = astman_get_header(m, "Conference");
02844 int res = 0;
02845
02846 if (ast_strlen_zero(conference)) {
02847 astman_send_error(s, m, "No Conference name provided.");
02848 return 0;
02849 }
02850 if (!ao2_container_count(conference_bridges)) {
02851 astman_send_error(s, m, "No active conferences.");
02852 return 0;
02853 }
02854 if ((res = generic_lock_unlock_helper(lock, conference))) {
02855 astman_send_error(s, m, "No Conference by that name found.");
02856 return 0;
02857 }
02858 astman_send_ack(s, m, lock ? "Conference locked" : "Conference unlocked");
02859 return 0;
02860 }
02861 static int action_confbridgeunlock(struct mansession *s, const struct message *m)
02862 {
02863 return action_lock_unlock_helper(s, m, 0);
02864 }
02865 static int action_confbridgelock(struct mansession *s, const struct message *m)
02866 {
02867 return action_lock_unlock_helper(s, m, 1);
02868 }
02869
02870 static int action_confbridgekick(struct mansession *s, const struct message *m)
02871 {
02872 const char *conference = astman_get_header(m, "Conference");
02873 const char *channel = astman_get_header(m, "Channel");
02874 struct conference_bridge *bridge = NULL;
02875 struct conference_bridge tmp;
02876 int found;
02877
02878 if (ast_strlen_zero(conference)) {
02879 astman_send_error(s, m, "No Conference name provided.");
02880 return 0;
02881 }
02882 if (!ao2_container_count(conference_bridges)) {
02883 astman_send_error(s, m, "No active conferences.");
02884 return 0;
02885 }
02886
02887 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
02888 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02889 if (!bridge) {
02890 astman_send_error(s, m, "No Conference by that name found.");
02891 return 0;
02892 }
02893
02894 found = !kick_conference_participant(bridge, channel);
02895 ao2_ref(bridge, -1);
02896
02897 if (found) {
02898 astman_send_ack(s, m, "User kicked");
02899 } else {
02900 astman_send_error(s, m, "No Channel by that name found in Conference.");
02901 }
02902 return 0;
02903 }
02904
02905 static int action_confbridgestartrecord(struct mansession *s, const struct message *m)
02906 {
02907 const char *conference = astman_get_header(m, "Conference");
02908 const char *recordfile = astman_get_header(m, "RecordFile");
02909 struct conference_bridge *bridge = NULL;
02910 struct conference_bridge tmp;
02911
02912 if (ast_strlen_zero(conference)) {
02913 astman_send_error(s, m, "No Conference name provided.");
02914 return 0;
02915 }
02916 if (!ao2_container_count(conference_bridges)) {
02917 astman_send_error(s, m, "No active conferences.");
02918 return 0;
02919 }
02920
02921 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
02922 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02923 if (!bridge) {
02924 astman_send_error(s, m, "No Conference by that name found.");
02925 return 0;
02926 }
02927
02928 ao2_lock(bridge);
02929 if (conf_is_recording(bridge)) {
02930 astman_send_error(s, m, "Conference is already being recorded.");
02931 ao2_unlock(bridge);
02932 ao2_ref(bridge, -1);
02933 return 0;
02934 }
02935
02936 if (!ast_strlen_zero(recordfile)) {
02937 ast_copy_string(bridge->b_profile.rec_file, recordfile, sizeof(bridge->b_profile.rec_file));
02938 }
02939
02940 if (start_conf_record_thread(bridge)) {
02941 astman_send_error(s, m, "Internal error starting conference recording.");
02942 ao2_unlock(bridge);
02943 ao2_ref(bridge, -1);
02944 return 0;
02945 }
02946 ao2_unlock(bridge);
02947
02948 ao2_ref(bridge, -1);
02949 astman_send_ack(s, m, "Conference Recording Started.");
02950 return 0;
02951 }
02952 static int action_confbridgestoprecord(struct mansession *s, const struct message *m)
02953 {
02954 const char *conference = astman_get_header(m, "Conference");
02955 struct conference_bridge *bridge = NULL;
02956 struct conference_bridge tmp;
02957
02958 if (ast_strlen_zero(conference)) {
02959 astman_send_error(s, m, "No Conference name provided.");
02960 return 0;
02961 }
02962 if (!ao2_container_count(conference_bridges)) {
02963 astman_send_error(s, m, "No active conferences.");
02964 return 0;
02965 }
02966
02967 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
02968 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
02969 if (!bridge) {
02970 astman_send_error(s, m, "No Conference by that name found.");
02971 return 0;
02972 }
02973
02974 ao2_lock(bridge);
02975 if (conf_stop_record(bridge)) {
02976 ao2_unlock(bridge);
02977 astman_send_error(s, m, "Internal error while stopping recording.");
02978 ao2_ref(bridge, -1);
02979 return 0;
02980 }
02981 ao2_unlock(bridge);
02982
02983 ao2_ref(bridge, -1);
02984 astman_send_ack(s, m, "Conference Recording Stopped.");
02985 return 0;
02986 }
02987
02988 static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m)
02989 {
02990 const char *conference = astman_get_header(m, "Conference");
02991 const char *channel = astman_get_header(m, "Channel");
02992 struct conference_bridge_user *participant = NULL;
02993 struct conference_bridge *bridge = NULL;
02994 struct conference_bridge tmp;
02995
02996 if (ast_strlen_zero(conference)) {
02997 astman_send_error(s, m, "No Conference name provided.");
02998 return 0;
02999 }
03000 if (ast_strlen_zero(channel)) {
03001 astman_send_error(s, m, "No channel name provided.");
03002 return 0;
03003 }
03004 if (!ao2_container_count(conference_bridges)) {
03005 astman_send_error(s, m, "No active conferences.");
03006 return 0;
03007 }
03008
03009 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
03010 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
03011 if (!bridge) {
03012 astman_send_error(s, m, "No Conference by that name found.");
03013 return 0;
03014 }
03015
03016
03017 ao2_lock(bridge);
03018 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
03019 if (!strncmp(channel, ast_channel_name(participant->chan), strlen(channel))) {
03020 ast_bridge_set_single_src_video_mode(bridge->bridge, participant->chan);
03021 break;
03022 }
03023 }
03024 ao2_unlock(bridge);
03025 ao2_ref(bridge, -1);
03026
03027
03028
03029 if (!participant) {
03030 astman_send_error(s, m, "No channel by that name found in conference.");
03031 return 0;
03032 }
03033 astman_send_ack(s, m, "Conference single video source set.");
03034 return 0;
03035 }
03036
03037 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
03038 {
03039 char *parse = NULL;
03040 struct conference_bridge *bridge = NULL;
03041 struct conference_bridge_user *participant = NULL;
03042 struct conference_bridge tmp;
03043 int count = 0;
03044 AST_DECLARE_APP_ARGS(args,
03045 AST_APP_ARG(type);
03046 AST_APP_ARG(confno);
03047 );
03048
03049
03050 if (ast_strlen_zero(data)) {
03051 return -1;
03052 }
03053 parse = ast_strdupa(data);
03054 AST_STANDARD_APP_ARGS(args, parse);
03055 if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
03056 return -1;
03057 }
03058 if (!ao2_container_count(conference_bridges)) {
03059 snprintf(buf, len, "0");
03060 return 0;
03061 }
03062 ast_copy_string(tmp.name, args.confno, sizeof(tmp.name));
03063 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
03064 if (!bridge) {
03065 snprintf(buf, len, "0");
03066 return 0;
03067 }
03068
03069
03070 ao2_lock(bridge);
03071 if (!strncasecmp(args.type, "parties", 7)) {
03072 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
03073 count++;
03074 }
03075 AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) {
03076 count++;
03077 }
03078 } else if (!strncasecmp(args.type, "admins", 6)) {
03079 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
03080 if (ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
03081 count++;
03082 }
03083 }
03084 } else if (!strncasecmp(args.type, "marked", 6)) {
03085 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
03086 if (ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER)) {
03087 count++;
03088 }
03089 }
03090 } else if (!strncasecmp(args.type, "locked", 6)) {
03091 count = bridge->locked;
03092 } else {
03093 ast_log(LOG_ERROR, "Invalid keyword '%s' passed to CONFBRIDGE_INFO. Should be one of: "
03094 "parties, admins, marked, or locked.\n", args.type);
03095 }
03096 snprintf(buf, len, "%d", count);
03097 ao2_unlock(bridge);
03098 ao2_ref(bridge, -1);
03099 return 0;
03100 }
03101
03102 void conf_add_user_active(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
03103 {
03104 AST_LIST_INSERT_TAIL(&conference_bridge->active_list, cbu, list);
03105 conference_bridge->activeusers++;
03106 }
03107
03108 void conf_add_user_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
03109 {
03110 AST_LIST_INSERT_TAIL(&conference_bridge->active_list, cbu, list);
03111 conference_bridge->activeusers++;
03112 conference_bridge->markedusers++;
03113 }
03114
03115 void conf_add_user_waiting(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
03116 {
03117 AST_LIST_INSERT_TAIL(&conference_bridge->waiting_list, cbu, list);
03118 conference_bridge->waitingusers++;
03119 }
03120
03121 void conf_remove_user_active(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
03122 {
03123 AST_LIST_REMOVE(&conference_bridge->active_list, cbu, list);
03124 conference_bridge->activeusers--;
03125 }
03126
03127 void conf_remove_user_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
03128 {
03129 AST_LIST_REMOVE(&conference_bridge->active_list, cbu, list);
03130 conference_bridge->activeusers--;
03131 conference_bridge->markedusers--;
03132 }
03133
03134 void conf_mute_only_active(struct conference_bridge *conference_bridge)
03135 {
03136 struct conference_bridge_user *only_participant = AST_LIST_FIRST(&conference_bridge->active_list);
03137
03138
03139 if (ast_test_flag(&only_participant->u_profile, USER_OPT_MUSICONHOLD)) {
03140 conf_moh_start(only_participant);
03141 }
03142 conf_update_user_mute(only_participant);
03143 }
03144
03145 void conf_remove_user_waiting(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
03146 {
03147 AST_LIST_REMOVE(&conference_bridge->waiting_list, cbu, list);
03148 conference_bridge->waitingusers--;
03149 }
03150
03151
03152 static int unload_module(void)
03153 {
03154 int res = ast_unregister_application(app);
03155
03156 ast_custom_function_unregister(&confbridge_function);
03157 ast_custom_function_unregister(&confbridge_info_function);
03158
03159 ast_cli_unregister_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
03160
03161
03162 ao2_ref(conference_bridges, -1);
03163
03164 conf_destroy_config();
03165
03166 ast_channel_unregister(&record_tech);
03167 record_tech.capabilities = ast_format_cap_destroy(record_tech.capabilities);
03168
03169 res |= ast_manager_unregister("ConfbridgeList");
03170 res |= ast_manager_unregister("ConfbridgeListRooms");
03171 res |= ast_manager_unregister("ConfbridgeMute");
03172 res |= ast_manager_unregister("ConfbridgeUnmute");
03173 res |= ast_manager_unregister("ConfbridgeKick");
03174 res |= ast_manager_unregister("ConfbridgeUnlock");
03175 res |= ast_manager_unregister("ConfbridgeLock");
03176 res |= ast_manager_unregister("ConfbridgeStartRecord");
03177 res |= ast_manager_unregister("ConfbridgeStopRecord");
03178 res |= ast_manager_unregister("ConfbridgeSetSingleVideoSrc");
03179
03180 return res;
03181 }
03182
03183
03184 static int load_module(void)
03185 {
03186 int res = 0;
03187
03188 if (conf_load_config()) {
03189 ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
03190 return AST_MODULE_LOAD_DECLINE;
03191 }
03192 if ((ast_custom_function_register(&confbridge_function))) {
03193 return AST_MODULE_LOAD_FAILURE;
03194 }
03195 if ((ast_custom_function_register(&confbridge_info_function))) {
03196 return AST_MODULE_LOAD_FAILURE;
03197 }
03198 if (!(record_tech.capabilities = ast_format_cap_alloc())) {
03199 return AST_MODULE_LOAD_FAILURE;
03200 }
03201 ast_format_cap_add_all(record_tech.capabilities);
03202 if (ast_channel_register(&record_tech)) {
03203 ast_log(LOG_ERROR, "Unable to register ConfBridge recorder.\n");
03204 return AST_MODULE_LOAD_FAILURE;
03205 }
03206
03207 if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
03208 return AST_MODULE_LOAD_FAILURE;
03209 }
03210 if (ast_register_application_xml(app, confbridge_exec)) {
03211 ao2_ref(conference_bridges, -1);
03212 return AST_MODULE_LOAD_FAILURE;
03213 }
03214
03215 res |= ast_cli_register_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
03216 res |= ast_manager_register_xml("ConfbridgeList", EVENT_FLAG_REPORTING, action_confbridgelist);
03217 res |= ast_manager_register_xml("ConfbridgeListRooms", EVENT_FLAG_REPORTING, action_confbridgelistrooms);
03218 res |= ast_manager_register_xml("ConfbridgeMute", EVENT_FLAG_CALL, action_confbridgemute);
03219 res |= ast_manager_register_xml("ConfbridgeUnmute", EVENT_FLAG_CALL, action_confbridgeunmute);
03220 res |= ast_manager_register_xml("ConfbridgeKick", EVENT_FLAG_CALL, action_confbridgekick);
03221 res |= ast_manager_register_xml("ConfbridgeUnlock", EVENT_FLAG_CALL, action_confbridgeunlock);
03222 res |= ast_manager_register_xml("ConfbridgeLock", EVENT_FLAG_CALL, action_confbridgelock);
03223 res |= ast_manager_register_xml("ConfbridgeStartRecord", EVENT_FLAG_CALL, action_confbridgestartrecord);
03224 res |= ast_manager_register_xml("ConfbridgeStopRecord", EVENT_FLAG_CALL, action_confbridgestoprecord);
03225 res |= ast_manager_register_xml("ConfbridgeSetSingleVideoSrc", EVENT_FLAG_CALL, action_confbridgesetsinglevideosrc);
03226 if (res) {
03227 return AST_MODULE_LOAD_FAILURE;
03228 }
03229
03230 return AST_MODULE_LOAD_SUCCESS;
03231 }
03232
03233 static int reload(void)
03234 {
03235 return conf_reload_config();
03236 }
03237
03238 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Conference Bridge Application",
03239 .load = load_module,
03240 .unload = unload_module,
03241 .reload = reload,
03242 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
03243 );