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
00036
00037 #include "asterisk.h"
00038
00039 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 367906 $")
00040
00041 #include <dahdi/user.h>
00042
00043 #include "asterisk/lock.h"
00044 #include "asterisk/file.h"
00045 #include "asterisk/channel.h"
00046 #include "asterisk/pbx.h"
00047 #include "asterisk/module.h"
00048 #include "asterisk/config.h"
00049 #include "asterisk/app.h"
00050 #include "asterisk/dsp.h"
00051 #include "asterisk/musiconhold.h"
00052 #include "asterisk/manager.h"
00053 #include "asterisk/cli.h"
00054 #include "asterisk/say.h"
00055 #include "asterisk/utils.h"
00056 #include "asterisk/translate.h"
00057 #include "asterisk/ulaw.h"
00058 #include "asterisk/astobj2.h"
00059 #include "asterisk/devicestate.h"
00060 #include "asterisk/dial.h"
00061 #include "asterisk/causes.h"
00062 #include "asterisk/paths.h"
00063 #include "asterisk/data.h"
00064 #include "asterisk/test.h"
00065
00066 #include "enter.h"
00067 #include "leave.h"
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
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515 #define CONFIG_FILE_NAME "meetme.conf"
00516 #define SLA_CONFIG_FILE "sla.conf"
00517
00518
00519 #define DEFAULT_AUDIO_BUFFERS 32
00520
00521
00522 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
00523
00524 enum {
00525 ADMINFLAG_MUTED = (1 << 1),
00526 ADMINFLAG_SELFMUTED = (1 << 2),
00527 ADMINFLAG_KICKME = (1 << 3),
00528
00529 ADMINFLAG_T_REQUEST = (1 << 4),
00530 };
00531
00532 #define MEETME_DELAYDETECTTALK 300
00533 #define MEETME_DELAYDETECTENDTALK 1000
00534
00535 #define AST_FRAME_BITS 32
00536
00537 enum volume_action {
00538 VOL_UP,
00539 VOL_DOWN
00540 };
00541
00542 enum entrance_sound {
00543 ENTER,
00544 LEAVE
00545 };
00546
00547 enum recording_state {
00548 MEETME_RECORD_OFF,
00549 MEETME_RECORD_STARTED,
00550 MEETME_RECORD_ACTIVE,
00551 MEETME_RECORD_TERMINATE
00552 };
00553
00554 #define CONF_SIZE 320
00555
00556 enum {
00557
00558 CONFFLAG_ADMIN = (1 << 0),
00559
00560 CONFFLAG_MONITOR = (1 << 1),
00561
00562 CONFFLAG_KEYEXIT = (1 << 2),
00563
00564 CONFFLAG_STARMENU = (1 << 3),
00565
00566 CONFFLAG_TALKER = (1 << 4),
00567
00568 CONFFLAG_QUIET = (1 << 5),
00569
00570
00571 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
00572
00573 CONFFLAG_AGI = (1 << 7),
00574
00575 CONFFLAG_MOH = (1 << 8),
00576
00577 CONFFLAG_MARKEDEXIT = (1 << 9),
00578
00579 CONFFLAG_WAITMARKED = (1 << 10),
00580
00581 CONFFLAG_EXIT_CONTEXT = (1 << 11),
00582
00583 CONFFLAG_MARKEDUSER = (1 << 12),
00584
00585 CONFFLAG_INTROUSER = (1 << 13),
00586
00587 CONFFLAG_RECORDCONF = (1<< 14),
00588
00589 CONFFLAG_MONITORTALKER = (1 << 15),
00590 CONFFLAG_DYNAMIC = (1 << 16),
00591 CONFFLAG_DYNAMICPIN = (1 << 17),
00592 CONFFLAG_EMPTY = (1 << 18),
00593 CONFFLAG_EMPTYNOPIN = (1 << 19),
00594 CONFFLAG_ALWAYSPROMPT = (1 << 20),
00595
00596 CONFFLAG_OPTIMIZETALKER = (1 << 21),
00597
00598
00599 CONFFLAG_NOONLYPERSON = (1 << 22),
00600
00601
00602 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
00603
00604 CONFFLAG_STARTMUTED = (1 << 24),
00605
00606 CONFFLAG_PASS_DTMF = (1 << 25),
00607 CONFFLAG_SLA_STATION = (1 << 26),
00608 CONFFLAG_SLA_TRUNK = (1 << 27),
00609
00610 CONFFLAG_KICK_CONTINUE = (1 << 28),
00611 CONFFLAG_DURATION_STOP = (1 << 29),
00612 CONFFLAG_DURATION_LIMIT = (1 << 30),
00613 };
00614
00615
00616 #define CONFFLAG_NO_AUDIO_UNTIL_UP (1ULL << 31)
00617
00618 #define CONFFLAG_INTROMSG (1ULL << 32)
00619
00620 enum {
00621 OPT_ARG_WAITMARKED = 0,
00622 OPT_ARG_EXITKEYS = 1,
00623 OPT_ARG_DURATION_STOP = 2,
00624 OPT_ARG_DURATION_LIMIT = 3,
00625 OPT_ARG_MOH_CLASS = 4,
00626 OPT_ARG_INTROMSG = 5,
00627 OPT_ARG_ARRAY_SIZE = 6,
00628 };
00629
00630 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
00631 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
00632 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
00633 AST_APP_OPTION('b', CONFFLAG_AGI ),
00634 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
00635 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
00636 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
00637 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
00638 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
00639 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
00640 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
00641 AST_APP_OPTION_ARG('G', CONFFLAG_INTROMSG, OPT_ARG_INTROMSG ),
00642 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
00643 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
00644 AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
00645 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
00646 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
00647 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
00648 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
00649 AST_APP_OPTION('q', CONFFLAG_QUIET ),
00650 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
00651 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
00652 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
00653 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
00654 AST_APP_OPTION('t', CONFFLAG_TALKER ),
00655 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
00656 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
00657 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
00658 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
00659 AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
00660 AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
00661 END_OPTIONS );
00662
00663 static const char * const app = "MeetMe";
00664 static const char * const app2 = "MeetMeCount";
00665 static const char * const app3 = "MeetMeAdmin";
00666 static const char * const app4 = "MeetMeChannelAdmin";
00667 static const char * const slastation_app = "SLAStation";
00668 static const char * const slatrunk_app = "SLATrunk";
00669
00670
00671 static int rt_schedule;
00672 static int fuzzystart;
00673 static int earlyalert;
00674 static int endalert;
00675 static int extendby;
00676
00677
00678 static int rt_log_members;
00679
00680 #define MAX_CONFNUM 80
00681 #define MAX_PIN 80
00682 #define OPTIONS_LEN 100
00683
00684
00685 #define MAX_SETTINGS (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)
00686
00687 enum announcetypes {
00688 CONF_HASJOIN,
00689 CONF_HASLEFT
00690 };
00691
00692 struct announce_listitem {
00693 AST_LIST_ENTRY(announce_listitem) entry;
00694 char namerecloc[PATH_MAX];
00695 char language[MAX_LANGUAGE];
00696 struct ast_channel *confchan;
00697 int confusers;
00698 enum announcetypes announcetype;
00699 };
00700
00701
00702 struct ast_conference {
00703 ast_mutex_t playlock;
00704 ast_mutex_t listenlock;
00705 char confno[MAX_CONFNUM];
00706 struct ast_channel *chan;
00707 struct ast_channel *lchan;
00708 int fd;
00709 int dahdiconf;
00710 int users;
00711 int markedusers;
00712 int maxusers;
00713 int endalert;
00714 time_t start;
00715 int refcount;
00716 enum recording_state recording:2;
00717 unsigned int isdynamic:1;
00718 unsigned int locked:1;
00719 unsigned int gmuted:1;
00720 pthread_t recordthread;
00721 ast_mutex_t recordthreadlock;
00722 pthread_attr_t attr;
00723 char *recordingfilename;
00724 char *recordingformat;
00725 char pin[MAX_PIN];
00726 char pinadmin[MAX_PIN];
00727 char uniqueid[32];
00728 long endtime;
00729 const char *useropts;
00730 const char *adminopts;
00731 const char *bookid;
00732 struct ast_frame *transframe[32];
00733 struct ast_frame *origframe;
00734 struct ast_trans_pvt *transpath[32];
00735 struct ao2_container *usercontainer;
00736 AST_LIST_ENTRY(ast_conference) list;
00737
00738 pthread_t announcethread;
00739 ast_mutex_t announcethreadlock;
00740 unsigned int announcethread_stop:1;
00741 ast_cond_t announcelist_addition;
00742 AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
00743 ast_mutex_t announcelistlock;
00744 };
00745
00746 static AST_LIST_HEAD_STATIC(confs, ast_conference);
00747
00748 static unsigned int conf_map[1024] = {0, };
00749
00750 struct volume {
00751 int desired;
00752 int actual;
00753 };
00754
00755
00756 struct ast_conf_user {
00757 int user_no;
00758 struct ast_flags64 userflags;
00759 int adminflags;
00760 struct ast_channel *chan;
00761 int talking;
00762 int dahdichannel;
00763 char usrvalue[50];
00764 char namerecloc[PATH_MAX];
00765 time_t jointime;
00766 time_t kicktime;
00767 struct timeval start_time;
00768 long timelimit;
00769 long play_warning;
00770 long warning_freq;
00771 const char *warning_sound;
00772 const char *end_sound;
00773 struct volume talk;
00774 struct volume listen;
00775 AST_LIST_ENTRY(ast_conf_user) list;
00776 };
00777
00778 enum sla_which_trunk_refs {
00779 ALL_TRUNK_REFS,
00780 INACTIVE_TRUNK_REFS,
00781 };
00782
00783 enum sla_trunk_state {
00784 SLA_TRUNK_STATE_IDLE,
00785 SLA_TRUNK_STATE_RINGING,
00786 SLA_TRUNK_STATE_UP,
00787 SLA_TRUNK_STATE_ONHOLD,
00788 SLA_TRUNK_STATE_ONHOLD_BYME,
00789 };
00790
00791 enum sla_hold_access {
00792
00793
00794 SLA_HOLD_OPEN,
00795
00796
00797 SLA_HOLD_PRIVATE,
00798 };
00799
00800 struct sla_trunk_ref;
00801
00802 struct sla_station {
00803 AST_RWLIST_ENTRY(sla_station) entry;
00804 AST_DECLARE_STRING_FIELDS(
00805 AST_STRING_FIELD(name);
00806 AST_STRING_FIELD(device);
00807 AST_STRING_FIELD(autocontext);
00808 );
00809 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
00810 struct ast_dial *dial;
00811
00812
00813
00814 unsigned int ring_timeout;
00815
00816
00817
00818 unsigned int ring_delay;
00819
00820
00821 unsigned int hold_access:1;
00822
00823 unsigned int ref_count;
00824 };
00825
00826 struct sla_station_ref {
00827 AST_LIST_ENTRY(sla_station_ref) entry;
00828 struct sla_station *station;
00829 };
00830
00831 struct sla_trunk {
00832 AST_RWLIST_ENTRY(sla_trunk) entry;
00833 AST_DECLARE_STRING_FIELDS(
00834 AST_STRING_FIELD(name);
00835 AST_STRING_FIELD(device);
00836 AST_STRING_FIELD(autocontext);
00837 );
00838 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
00839
00840 unsigned int num_stations;
00841
00842 unsigned int active_stations;
00843
00844 unsigned int hold_stations;
00845 struct ast_channel *chan;
00846 unsigned int ring_timeout;
00847
00848
00849 unsigned int barge_disabled:1;
00850
00851
00852 unsigned int hold_access:1;
00853
00854
00855 unsigned int on_hold:1;
00856
00857 unsigned int ref_count;
00858 };
00859
00860 struct sla_trunk_ref {
00861 AST_LIST_ENTRY(sla_trunk_ref) entry;
00862 struct sla_trunk *trunk;
00863 enum sla_trunk_state state;
00864 struct ast_channel *chan;
00865
00866
00867
00868 unsigned int ring_timeout;
00869
00870
00871
00872 unsigned int ring_delay;
00873 };
00874
00875 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
00876 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
00877
00878 static const char sla_registrar[] = "SLA";
00879
00880
00881 enum sla_event_type {
00882
00883 SLA_EVENT_HOLD,
00884
00885 SLA_EVENT_DIAL_STATE,
00886
00887 SLA_EVENT_RINGING_TRUNK,
00888
00889 SLA_EVENT_RELOAD,
00890
00891 SLA_EVENT_CHECK_RELOAD,
00892 };
00893
00894 struct sla_event {
00895 enum sla_event_type type;
00896 struct sla_station *station;
00897 struct sla_trunk_ref *trunk_ref;
00898 AST_LIST_ENTRY(sla_event) entry;
00899 };
00900
00901
00902
00903 struct sla_failed_station {
00904 struct sla_station *station;
00905 struct timeval last_try;
00906 AST_LIST_ENTRY(sla_failed_station) entry;
00907 };
00908
00909
00910 struct sla_ringing_trunk {
00911 struct sla_trunk *trunk;
00912
00913 struct timeval ring_begin;
00914 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
00915 AST_LIST_ENTRY(sla_ringing_trunk) entry;
00916 };
00917
00918 enum sla_station_hangup {
00919 SLA_STATION_HANGUP_NORMAL,
00920 SLA_STATION_HANGUP_TIMEOUT,
00921 };
00922
00923
00924 struct sla_ringing_station {
00925 struct sla_station *station;
00926
00927 struct timeval ring_begin;
00928 AST_LIST_ENTRY(sla_ringing_station) entry;
00929 };
00930
00931
00932
00933
00934 static struct {
00935
00936 pthread_t thread;
00937 ast_cond_t cond;
00938 ast_mutex_t lock;
00939 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
00940 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
00941 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
00942 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
00943 unsigned int stop:1;
00944
00945
00946 unsigned int attempt_callerid:1;
00947
00948 unsigned int reload:1;
00949 } sla = {
00950 .thread = AST_PTHREADT_NULL,
00951 };
00952
00953
00954
00955 static int audio_buffers;
00956
00957
00958
00959
00960
00961
00962
00963
00964 static const char gain_map[] = {
00965 -15,
00966 -13,
00967 -10,
00968 -6,
00969 0,
00970 0,
00971 0,
00972 6,
00973 10,
00974 13,
00975 15,
00976 };
00977
00978
00979 static int admin_exec(struct ast_channel *chan, const char *data);
00980 static void *recordthread(void *args);
00981
00982 static const char *istalking(int x)
00983 {
00984 if (x > 0)
00985 return "(talking)";
00986 else if (x < 0)
00987 return "(unmonitored)";
00988 else
00989 return "(not talking)";
00990 }
00991
00992 static int careful_write(int fd, unsigned char *data, int len, int block)
00993 {
00994 int res;
00995 int x;
00996
00997 while (len) {
00998 if (block) {
00999 x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
01000 res = ioctl(fd, DAHDI_IOMUX, &x);
01001 } else
01002 res = 0;
01003 if (res >= 0)
01004 res = write(fd, data, len);
01005 if (res < 1) {
01006 if (errno != EAGAIN) {
01007 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
01008 return -1;
01009 } else
01010 return 0;
01011 }
01012 len -= res;
01013 data += res;
01014 }
01015
01016 return 0;
01017 }
01018
01019 static int set_talk_volume(struct ast_conf_user *user, int volume)
01020 {
01021 char gain_adjust;
01022
01023
01024
01025
01026 gain_adjust = gain_map[volume + 5];
01027
01028 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
01029 }
01030
01031 static int set_listen_volume(struct ast_conf_user *user, int volume)
01032 {
01033 char gain_adjust;
01034
01035
01036
01037
01038 gain_adjust = gain_map[volume + 5];
01039
01040 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
01041 }
01042
01043 static void tweak_volume(struct volume *vol, enum volume_action action)
01044 {
01045 switch (action) {
01046 case VOL_UP:
01047 switch (vol->desired) {
01048 case 5:
01049 break;
01050 case 0:
01051 vol->desired = 2;
01052 break;
01053 case -2:
01054 vol->desired = 0;
01055 break;
01056 default:
01057 vol->desired++;
01058 break;
01059 }
01060 break;
01061 case VOL_DOWN:
01062 switch (vol->desired) {
01063 case -5:
01064 break;
01065 case 2:
01066 vol->desired = 0;
01067 break;
01068 case 0:
01069 vol->desired = -2;
01070 break;
01071 default:
01072 vol->desired--;
01073 break;
01074 }
01075 }
01076 }
01077
01078 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
01079 {
01080 tweak_volume(&user->talk, action);
01081
01082
01083
01084 if (!set_talk_volume(user, user->talk.desired))
01085 user->talk.actual = 0;
01086 else
01087 user->talk.actual = user->talk.desired;
01088 }
01089
01090 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
01091 {
01092 tweak_volume(&user->listen, action);
01093
01094
01095
01096 if (!set_listen_volume(user, user->listen.desired))
01097 user->listen.actual = 0;
01098 else
01099 user->listen.actual = user->listen.desired;
01100 }
01101
01102 static void reset_volumes(struct ast_conf_user *user)
01103 {
01104 signed char zero_volume = 0;
01105
01106 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
01107 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
01108 }
01109
01110 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
01111 {
01112 unsigned char *data;
01113 int len;
01114 int res = -1;
01115
01116 if (!ast_check_hangup(chan))
01117 res = ast_autoservice_start(chan);
01118
01119 AST_LIST_LOCK(&confs);
01120
01121 switch(sound) {
01122 case ENTER:
01123 data = enter;
01124 len = sizeof(enter);
01125 break;
01126 case LEAVE:
01127 data = leave;
01128 len = sizeof(leave);
01129 break;
01130 default:
01131 data = NULL;
01132 len = 0;
01133 }
01134 if (data) {
01135 careful_write(conf->fd, data, len, 1);
01136 }
01137
01138 AST_LIST_UNLOCK(&confs);
01139
01140 if (!res)
01141 ast_autoservice_stop(chan);
01142 }
01143
01144 static int user_no_cmp(void *obj, void *arg, int flags)
01145 {
01146 struct ast_conf_user *user = obj;
01147 int *user_no = arg;
01148
01149 if (user->user_no == *user_no) {
01150 return (CMP_MATCH | CMP_STOP);
01151 }
01152
01153 return 0;
01154 }
01155
01156 static int user_max_cmp(void *obj, void *arg, int flags)
01157 {
01158 struct ast_conf_user *user = obj;
01159 int *max_no = arg;
01160
01161 if (user->user_no > *max_no) {
01162 *max_no = user->user_no;
01163 }
01164
01165 return 0;
01166 }
01167
01168
01169
01170
01171
01172
01173
01174
01175
01176
01177
01178
01179
01180
01181
01182 static struct ast_conference *build_conf(const char *confno, const char *pin,
01183 const char *pinadmin, int make, int dynamic, int refcount,
01184 const struct ast_channel *chan, struct ast_test *test)
01185 {
01186 struct ast_conference *cnf;
01187 struct dahdi_confinfo dahdic = { 0, };
01188 int confno_int = 0;
01189
01190 AST_LIST_LOCK(&confs);
01191
01192 AST_LIST_TRAVERSE(&confs, cnf, list) {
01193 if (!strcmp(confno, cnf->confno))
01194 break;
01195 }
01196
01197 if (cnf || (!make && !dynamic))
01198 goto cnfout;
01199
01200
01201 if (!(cnf = ast_calloc(1, sizeof(*cnf))) ||
01202 !(cnf->usercontainer = ao2_container_alloc(1, NULL, user_no_cmp))) {
01203 goto cnfout;
01204 }
01205
01206 ast_mutex_init(&cnf->playlock);
01207 ast_mutex_init(&cnf->listenlock);
01208 cnf->recordthread = AST_PTHREADT_NULL;
01209 ast_mutex_init(&cnf->recordthreadlock);
01210 cnf->announcethread = AST_PTHREADT_NULL;
01211 ast_mutex_init(&cnf->announcethreadlock);
01212 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
01213 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
01214 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
01215 ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
01216
01217
01218 dahdic.confno = -1;
01219 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
01220 cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
01221 if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
01222 if (test) {
01223
01224
01225
01226 ast_test_status_update(test, "Unable to open DAHDI pseudo device\n");
01227 } else {
01228 ast_log(LOG_WARNING, "Unable to open DAHDI pseudo device\n");
01229 if (cnf->fd >= 0)
01230 close(cnf->fd);
01231 ao2_ref(cnf->usercontainer, -1);
01232 ast_mutex_destroy(&cnf->playlock);
01233 ast_mutex_destroy(&cnf->listenlock);
01234 ast_mutex_destroy(&cnf->recordthreadlock);
01235 ast_mutex_destroy(&cnf->announcethreadlock);
01236 ast_free(cnf);
01237 cnf = NULL;
01238 goto cnfout;
01239 }
01240 }
01241
01242 cnf->dahdiconf = dahdic.confno;
01243
01244
01245 cnf->chan = ast_request("DAHDI", AST_FORMAT_SLINEAR, chan, "pseudo", NULL);
01246 if (cnf->chan) {
01247 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
01248 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
01249 dahdic.chan = 0;
01250 dahdic.confno = cnf->dahdiconf;
01251 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
01252 if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &dahdic)) {
01253 if (test) {
01254 ast_test_status_update(test, "Error setting conference on pseudo channel\n");
01255 }
01256 ast_log(LOG_WARNING, "Error setting conference\n");
01257 if (cnf->chan)
01258 ast_hangup(cnf->chan);
01259 else
01260 close(cnf->fd);
01261 ao2_ref(cnf->usercontainer, -1);
01262 ast_mutex_destroy(&cnf->playlock);
01263 ast_mutex_destroy(&cnf->listenlock);
01264 ast_mutex_destroy(&cnf->recordthreadlock);
01265 ast_mutex_destroy(&cnf->announcethreadlock);
01266 ast_free(cnf);
01267 cnf = NULL;
01268 goto cnfout;
01269 }
01270 }
01271
01272
01273 cnf->start = time(NULL);
01274 cnf->maxusers = 0x7fffffff;
01275 cnf->isdynamic = dynamic ? 1 : 0;
01276 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
01277 AST_LIST_INSERT_HEAD(&confs, cnf, list);
01278
01279
01280 if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
01281 conf_map[confno_int] = 1;
01282
01283 cnfout:
01284 if (cnf)
01285 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
01286
01287 AST_LIST_UNLOCK(&confs);
01288
01289 return cnf;
01290 }
01291
01292 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
01293 {
01294 static const char * const cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
01295
01296 int len = strlen(word);
01297 int which = 0;
01298 struct ast_conference *cnf = NULL;
01299 struct ast_conf_user *usr = NULL;
01300 char *confno = NULL;
01301 char usrno[50] = "";
01302 char *myline, *ret = NULL;
01303
01304 if (pos == 1) {
01305 return ast_cli_complete(word, cmds, state);
01306 } else if (pos == 2) {
01307 AST_LIST_LOCK(&confs);
01308 AST_LIST_TRAVERSE(&confs, cnf, list) {
01309 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
01310 ret = cnf->confno;
01311 break;
01312 }
01313 }
01314 ret = ast_strdup(ret);
01315 AST_LIST_UNLOCK(&confs);
01316 return ret;
01317 } else if (pos == 3) {
01318
01319 if (strstr(line, "mute") || strstr(line, "kick")) {
01320 if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
01321 return ast_strdup("all");
01322 which++;
01323 AST_LIST_LOCK(&confs);
01324
01325
01326 myline = ast_strdupa(line);
01327 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
01328 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
01329 ;
01330 }
01331
01332 AST_LIST_TRAVERSE(&confs, cnf, list) {
01333 if (!strcmp(confno, cnf->confno))
01334 break;
01335 }
01336
01337 if (cnf) {
01338 struct ao2_iterator user_iter;
01339 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
01340
01341 while((usr = ao2_iterator_next(&user_iter))) {
01342 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
01343 if (!strncasecmp(word, usrno, len) && ++which > state) {
01344 ao2_ref(usr, -1);
01345 break;
01346 }
01347 ao2_ref(usr, -1);
01348 }
01349 ao2_iterator_destroy(&user_iter);
01350 AST_LIST_UNLOCK(&confs);
01351 return usr ? ast_strdup(usrno) : NULL;
01352 }
01353 AST_LIST_UNLOCK(&confs);
01354 }
01355 }
01356
01357 return NULL;
01358 }
01359
01360 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01361 {
01362
01363 struct ast_conf_user *user;
01364 struct ast_conference *cnf;
01365 int hr, min, sec;
01366 int i = 0, total = 0;
01367 time_t now;
01368 struct ast_str *cmdline = NULL;
01369 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
01370 #define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
01371
01372 switch (cmd) {
01373 case CLI_INIT:
01374 e->command = "meetme list [concise]";
01375 e->usage =
01376 "Usage: meetme list [concise] <confno> \n"
01377 " List all or a specific conference.\n";
01378 return NULL;
01379 case CLI_GENERATE:
01380 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
01381 }
01382
01383
01384 for (i = 0; i < a->argc; i++) {
01385 if (strlen(a->argv[i]) > 100)
01386 ast_cli(a->fd, "Invalid Arguments.\n");
01387 }
01388
01389
01390 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
01391 return CLI_FAILURE;
01392 }
01393
01394 if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], "concise"))) {
01395
01396 int concise = (a->argc == 3 && !strcasecmp(a->argv[2], "concise"));
01397 now = time(NULL);
01398 AST_LIST_LOCK(&confs);
01399 if (AST_LIST_EMPTY(&confs)) {
01400 if (!concise) {
01401 ast_cli(a->fd, "No active MeetMe conferences.\n");
01402 }
01403 AST_LIST_UNLOCK(&confs);
01404 ast_free(cmdline);
01405 return CLI_SUCCESS;
01406 }
01407 if (!concise) {
01408 ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
01409 }
01410 AST_LIST_TRAVERSE(&confs, cnf, list) {
01411 if (cnf->markedusers == 0) {
01412 ast_str_set(&cmdline, 0, "N/A ");
01413 } else {
01414 ast_str_set(&cmdline, 0, "%4.4d", cnf->markedusers);
01415 }
01416 hr = (now - cnf->start) / 3600;
01417 min = ((now - cnf->start) % 3600) / 60;
01418 sec = (now - cnf->start) % 60;
01419 if (!concise) {
01420 ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users, ast_str_buffer(cmdline), hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
01421 } else {
01422 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
01423 cnf->confno,
01424 cnf->users,
01425 cnf->markedusers,
01426 hr, min, sec,
01427 cnf->isdynamic,
01428 cnf->locked);
01429 }
01430
01431 total += cnf->users;
01432 }
01433 AST_LIST_UNLOCK(&confs);
01434 if (!concise) {
01435 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
01436 }
01437 ast_free(cmdline);
01438 return CLI_SUCCESS;
01439 } else if (strcmp(a->argv[1], "list") == 0) {
01440 struct ao2_iterator user_iter;
01441 int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
01442
01443 if (AST_LIST_EMPTY(&confs)) {
01444 if (!concise) {
01445 ast_cli(a->fd, "No active MeetMe conferences.\n");
01446 }
01447 ast_free(cmdline);
01448 return CLI_SUCCESS;
01449 }
01450
01451 AST_LIST_LOCK(&confs);
01452 AST_LIST_TRAVERSE(&confs, cnf, list) {
01453 if (strcmp(cnf->confno, a->argv[2]) == 0) {
01454 break;
01455 }
01456 }
01457 if (!cnf) {
01458 if (!concise)
01459 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
01460 AST_LIST_UNLOCK(&confs);
01461 ast_free(cmdline);
01462 return CLI_SUCCESS;
01463 }
01464
01465 time(&now);
01466 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
01467 while((user = ao2_iterator_next(&user_iter))) {
01468 hr = (now - user->jointime) / 3600;
01469 min = ((now - user->jointime) % 3600) / 60;
01470 sec = (now - user->jointime) % 60;
01471 if (!concise) {
01472 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
01473 user->user_no,
01474 S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
01475 S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<no name>"),
01476 user->chan->name,
01477 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "(Admin)" : "",
01478 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "(Listen only)" : "",
01479 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
01480 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
01481 istalking(user->talking), hr, min, sec);
01482 } else {
01483 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
01484 user->user_no,
01485 S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, ""),
01486 S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, ""),
01487 user->chan->name,
01488 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "1" : "",
01489 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "1" : "",
01490 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
01491 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
01492 user->talking, hr, min, sec);
01493 }
01494 ao2_ref(user, -1);
01495 }
01496 ao2_iterator_destroy(&user_iter);
01497 if (!concise) {
01498 ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
01499 }
01500 AST_LIST_UNLOCK(&confs);
01501 ast_free(cmdline);
01502 return CLI_SUCCESS;
01503 }
01504 if (a->argc < 2) {
01505 ast_free(cmdline);
01506 return CLI_SHOWUSAGE;
01507 }
01508
01509 ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
01510
01511 admin_exec(NULL, ast_str_buffer(cmdline));
01512 ast_free(cmdline);
01513
01514 return CLI_SUCCESS;
01515 }
01516
01517
01518 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01519 {
01520
01521 struct ast_str *cmdline = NULL;
01522 int i = 0;
01523
01524 switch (cmd) {
01525 case CLI_INIT:
01526 e->command = "meetme {lock|unlock|mute|unmute|kick}";
01527 e->usage =
01528 "Usage: meetme (un)lock|(un)mute|kick <confno> <usernumber>\n"
01529 " Executes a command for the conference or on a conferee\n";
01530 return NULL;
01531 case CLI_GENERATE:
01532 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
01533 }
01534
01535 if (a->argc > 8)
01536 ast_cli(a->fd, "Invalid Arguments.\n");
01537
01538 for (i = 0; i < a->argc; i++) {
01539 if (strlen(a->argv[i]) > 100)
01540 ast_cli(a->fd, "Invalid Arguments.\n");
01541 }
01542
01543
01544 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
01545 return CLI_FAILURE;
01546 }
01547
01548 if (a->argc < 1) {
01549 ast_free(cmdline);
01550 return CLI_SHOWUSAGE;
01551 }
01552
01553 ast_str_set(&cmdline, 0, "%s", a->argv[2]);
01554 if (strstr(a->argv[1], "lock")) {
01555 if (strcmp(a->argv[1], "lock") == 0) {
01556
01557 ast_str_append(&cmdline, 0, ",L");
01558 } else {
01559
01560 ast_str_append(&cmdline, 0, ",l");
01561 }
01562 } else if (strstr(a->argv[1], "mute")) {
01563 if (a->argc < 4) {
01564 ast_free(cmdline);
01565 return CLI_SHOWUSAGE;
01566 }
01567 if (strcmp(a->argv[1], "mute") == 0) {
01568
01569 if (strcmp(a->argv[3], "all") == 0) {
01570 ast_str_append(&cmdline, 0, ",N");
01571 } else {
01572 ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);
01573 }
01574 } else {
01575
01576 if (strcmp(a->argv[3], "all") == 0) {
01577 ast_str_append(&cmdline, 0, ",n");
01578 } else {
01579 ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
01580 }
01581 }
01582 } else if (strcmp(a->argv[1], "kick") == 0) {
01583 if (a->argc < 4) {
01584 ast_free(cmdline);
01585 return CLI_SHOWUSAGE;
01586 }
01587 if (strcmp(a->argv[3], "all") == 0) {
01588
01589 ast_str_append(&cmdline, 0, ",K");
01590 } else {
01591
01592 ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
01593 }
01594 } else {
01595 ast_free(cmdline);
01596 return CLI_SHOWUSAGE;
01597 }
01598
01599 ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
01600
01601 admin_exec(NULL, ast_str_buffer(cmdline));
01602 ast_free(cmdline);
01603
01604 return CLI_SUCCESS;
01605 }
01606
01607 static const char *sla_hold_str(unsigned int hold_access)
01608 {
01609 const char *hold = "Unknown";
01610
01611 switch (hold_access) {
01612 case SLA_HOLD_OPEN:
01613 hold = "Open";
01614 break;
01615 case SLA_HOLD_PRIVATE:
01616 hold = "Private";
01617 default:
01618 break;
01619 }
01620
01621 return hold;
01622 }
01623
01624 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01625 {
01626 const struct sla_trunk *trunk;
01627
01628 switch (cmd) {
01629 case CLI_INIT:
01630 e->command = "sla show trunks";
01631 e->usage =
01632 "Usage: sla show trunks\n"
01633 " This will list all trunks defined in sla.conf\n";
01634 return NULL;
01635 case CLI_GENERATE:
01636 return NULL;
01637 }
01638
01639 ast_cli(a->fd, "\n"
01640 "=============================================================\n"
01641 "=== Configured SLA Trunks ===================================\n"
01642 "=============================================================\n"
01643 "===\n");
01644 AST_RWLIST_RDLOCK(&sla_trunks);
01645 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
01646 struct sla_station_ref *station_ref;
01647 char ring_timeout[16] = "(none)";
01648 if (trunk->ring_timeout)
01649 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
01650 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
01651 "=== Trunk Name: %s\n"
01652 "=== ==> Device: %s\n"
01653 "=== ==> AutoContext: %s\n"
01654 "=== ==> RingTimeout: %s\n"
01655 "=== ==> BargeAllowed: %s\n"
01656 "=== ==> HoldAccess: %s\n"
01657 "=== ==> Stations ...\n",
01658 trunk->name, trunk->device,
01659 S_OR(trunk->autocontext, "(none)"),
01660 ring_timeout,
01661 trunk->barge_disabled ? "No" : "Yes",
01662 sla_hold_str(trunk->hold_access));
01663 AST_RWLIST_RDLOCK(&sla_stations);
01664 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
01665 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
01666 AST_RWLIST_UNLOCK(&sla_stations);
01667 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
01668 }
01669 AST_RWLIST_UNLOCK(&sla_trunks);
01670 ast_cli(a->fd, "=============================================================\n\n");
01671
01672 return CLI_SUCCESS;
01673 }
01674
01675 static const char *trunkstate2str(enum sla_trunk_state state)
01676 {
01677 #define S(e) case e: return # e;
01678 switch (state) {
01679 S(SLA_TRUNK_STATE_IDLE)
01680 S(SLA_TRUNK_STATE_RINGING)
01681 S(SLA_TRUNK_STATE_UP)
01682 S(SLA_TRUNK_STATE_ONHOLD)
01683 S(SLA_TRUNK_STATE_ONHOLD_BYME)
01684 }
01685 return "Uknown State";
01686 #undef S
01687 }
01688
01689 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01690 {
01691 const struct sla_station *station;
01692
01693 switch (cmd) {
01694 case CLI_INIT:
01695 e->command = "sla show stations";
01696 e->usage =
01697 "Usage: sla show stations\n"
01698 " This will list all stations defined in sla.conf\n";
01699 return NULL;
01700 case CLI_GENERATE:
01701 return NULL;
01702 }
01703
01704 ast_cli(a->fd, "\n"
01705 "=============================================================\n"
01706 "=== Configured SLA Stations =================================\n"
01707 "=============================================================\n"
01708 "===\n");
01709 AST_RWLIST_RDLOCK(&sla_stations);
01710 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
01711 struct sla_trunk_ref *trunk_ref;
01712 char ring_timeout[16] = "(none)";
01713 char ring_delay[16] = "(none)";
01714 if (station->ring_timeout) {
01715 snprintf(ring_timeout, sizeof(ring_timeout),
01716 "%u", station->ring_timeout);
01717 }
01718 if (station->ring_delay) {
01719 snprintf(ring_delay, sizeof(ring_delay),
01720 "%u", station->ring_delay);
01721 }
01722 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
01723 "=== Station Name: %s\n"
01724 "=== ==> Device: %s\n"
01725 "=== ==> AutoContext: %s\n"
01726 "=== ==> RingTimeout: %s\n"
01727 "=== ==> RingDelay: %s\n"
01728 "=== ==> HoldAccess: %s\n"
01729 "=== ==> Trunks ...\n",
01730 station->name, station->device,
01731 S_OR(station->autocontext, "(none)"),
01732 ring_timeout, ring_delay,
01733 sla_hold_str(station->hold_access));
01734 AST_RWLIST_RDLOCK(&sla_trunks);
01735 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
01736 if (trunk_ref->ring_timeout) {
01737 snprintf(ring_timeout, sizeof(ring_timeout),
01738 "%u", trunk_ref->ring_timeout);
01739 } else
01740 strcpy(ring_timeout, "(none)");
01741 if (trunk_ref->ring_delay) {
01742 snprintf(ring_delay, sizeof(ring_delay),
01743 "%u", trunk_ref->ring_delay);
01744 } else
01745 strcpy(ring_delay, "(none)");
01746 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
01747 "=== ==> State: %s\n"
01748 "=== ==> RingTimeout: %s\n"
01749 "=== ==> RingDelay: %s\n",
01750 trunk_ref->trunk->name,
01751 trunkstate2str(trunk_ref->state),
01752 ring_timeout, ring_delay);
01753 }
01754 AST_RWLIST_UNLOCK(&sla_trunks);
01755 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
01756 "===\n");
01757 }
01758 AST_RWLIST_UNLOCK(&sla_stations);
01759 ast_cli(a->fd, "============================================================\n"
01760 "\n");
01761
01762 return CLI_SUCCESS;
01763 }
01764
01765 static struct ast_cli_entry cli_meetme[] = {
01766 AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
01767 AST_CLI_DEFINE(meetme_show_cmd, "List all or one conference"),
01768 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
01769 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
01770 };
01771
01772 static void conf_flush(int fd, struct ast_channel *chan)
01773 {
01774 int x;
01775
01776
01777
01778
01779 if (chan) {
01780 struct ast_frame *f;
01781
01782
01783
01784
01785 while (ast_waitfor(chan, 1)) {
01786 f = ast_read(chan);
01787 if (f)
01788 ast_frfree(f);
01789 else
01790 break;
01791 }
01792 }
01793
01794
01795 x = DAHDI_FLUSH_ALL;
01796 if (ioctl(fd, DAHDI_FLUSH, &x))
01797 ast_log(LOG_WARNING, "Error flushing channel\n");
01798
01799 }
01800
01801
01802
01803
01804 static int conf_free(struct ast_conference *conf)
01805 {
01806 int x;
01807 struct announce_listitem *item;
01808
01809 AST_LIST_REMOVE(&confs, conf, list);
01810 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
01811
01812 if (conf->recording == MEETME_RECORD_ACTIVE) {
01813 conf->recording = MEETME_RECORD_TERMINATE;
01814 AST_LIST_UNLOCK(&confs);
01815 while (1) {
01816 usleep(1);
01817 AST_LIST_LOCK(&confs);
01818 if (conf->recording == MEETME_RECORD_OFF)
01819 break;
01820 AST_LIST_UNLOCK(&confs);
01821 }
01822 }
01823
01824 for (x = 0; x < AST_FRAME_BITS; x++) {
01825 if (conf->transframe[x])
01826 ast_frfree(conf->transframe[x]);
01827 if (conf->transpath[x])
01828 ast_translator_free_path(conf->transpath[x]);
01829 }
01830 if (conf->announcethread != AST_PTHREADT_NULL) {
01831 ast_mutex_lock(&conf->announcelistlock);
01832 conf->announcethread_stop = 1;
01833 ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
01834 ast_cond_signal(&conf->announcelist_addition);
01835 ast_mutex_unlock(&conf->announcelistlock);
01836 pthread_join(conf->announcethread, NULL);
01837
01838 while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
01839 ast_filedelete(item->namerecloc, NULL);
01840 ao2_ref(item, -1);
01841 }
01842 ast_mutex_destroy(&conf->announcelistlock);
01843 }
01844
01845 if (conf->origframe)
01846 ast_frfree(conf->origframe);
01847 if (conf->lchan)
01848 ast_hangup(conf->lchan);
01849 if (conf->chan)
01850 ast_hangup(conf->chan);
01851 if (conf->fd >= 0)
01852 close(conf->fd);
01853 if (conf->recordingfilename) {
01854 ast_free(conf->recordingfilename);
01855 }
01856 if (conf->usercontainer) {
01857 ao2_ref(conf->usercontainer, -1);
01858 }
01859 if (conf->recordingformat) {
01860 ast_free(conf->recordingformat);
01861 }
01862 ast_mutex_destroy(&conf->playlock);
01863 ast_mutex_destroy(&conf->listenlock);
01864 ast_mutex_destroy(&conf->recordthreadlock);
01865 ast_mutex_destroy(&conf->announcethreadlock);
01866 ast_free(conf);
01867
01868 return 0;
01869 }
01870
01871 static void conf_queue_dtmf(const struct ast_conference *conf,
01872 const struct ast_conf_user *sender, struct ast_frame *f)
01873 {
01874 struct ast_conf_user *user;
01875 struct ao2_iterator user_iter;
01876
01877 user_iter = ao2_iterator_init(conf->usercontainer, 0);
01878 while ((user = ao2_iterator_next(&user_iter))) {
01879 if (user == sender) {
01880 ao2_ref(user, -1);
01881 continue;
01882 }
01883 if (ast_write(user->chan, f) < 0)
01884 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
01885 ao2_ref(user, -1);
01886 }
01887 ao2_iterator_destroy(&user_iter);
01888 }
01889
01890 static void sla_queue_event_full(enum sla_event_type type,
01891 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
01892 {
01893 struct sla_event *event;
01894
01895 if (sla.thread == AST_PTHREADT_NULL) {
01896 return;
01897 }
01898
01899 if (!(event = ast_calloc(1, sizeof(*event))))
01900 return;
01901
01902 event->type = type;
01903 event->trunk_ref = trunk_ref;
01904 event->station = station;
01905
01906 if (!lock) {
01907 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
01908 return;
01909 }
01910
01911 ast_mutex_lock(&sla.lock);
01912 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
01913 ast_cond_signal(&sla.cond);
01914 ast_mutex_unlock(&sla.lock);
01915 }
01916
01917 static void sla_queue_event_nolock(enum sla_event_type type)
01918 {
01919 sla_queue_event_full(type, NULL, NULL, 0);
01920 }
01921
01922 static void sla_queue_event(enum sla_event_type type)
01923 {
01924 sla_queue_event_full(type, NULL, NULL, 1);
01925 }
01926
01927
01928 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
01929 struct ast_conference *conf)
01930 {
01931 struct sla_station *station;
01932 struct sla_trunk_ref *trunk_ref = NULL;
01933 char *trunk_name;
01934
01935 trunk_name = ast_strdupa(conf->confno);
01936 strsep(&trunk_name, "_");
01937 if (ast_strlen_zero(trunk_name)) {
01938 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
01939 return;
01940 }
01941
01942 AST_RWLIST_RDLOCK(&sla_stations);
01943 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
01944 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
01945 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
01946 break;
01947 }
01948 if (trunk_ref)
01949 break;
01950 }
01951 AST_RWLIST_UNLOCK(&sla_stations);
01952
01953 if (!trunk_ref) {
01954 ast_debug(1, "Trunk not found for event!\n");
01955 return;
01956 }
01957
01958 sla_queue_event_full(type, trunk_ref, station, 1);
01959 }
01960
01961
01962 static int dispose_conf(struct ast_conference *conf)
01963 {
01964 int res = 0;
01965 int confno_int = 0;
01966
01967 AST_LIST_LOCK(&confs);
01968 if (ast_atomic_dec_and_test(&conf->refcount)) {
01969
01970 if ((sscanf(conf->confno, "%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
01971 conf_map[confno_int] = 0;
01972 }
01973 conf_free(conf);
01974 res = 1;
01975 }
01976 AST_LIST_UNLOCK(&confs);
01977
01978 return res;
01979 }
01980
01981 static int rt_extend_conf(const char *confno)
01982 {
01983 char currenttime[32];
01984 char endtime[32];
01985 struct timeval now;
01986 struct ast_tm tm;
01987 struct ast_variable *var, *orig_var;
01988 char bookid[51];
01989
01990 if (!extendby) {
01991 return 0;
01992 }
01993
01994 now = ast_tvnow();
01995
01996 ast_localtime(&now, &tm, NULL);
01997 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
01998
01999 var = ast_load_realtime("meetme", "confno",
02000 confno, "startTime<= ", currenttime,
02001 "endtime>= ", currenttime, NULL);
02002
02003 orig_var = var;
02004
02005
02006 while (var) {
02007 if (!strcasecmp(var->name, "bookid")) {
02008 ast_copy_string(bookid, var->value, sizeof(bookid));
02009 }
02010 if (!strcasecmp(var->name, "endtime")) {
02011 ast_copy_string(endtime, var->value, sizeof(endtime));
02012 }
02013
02014 var = var->next;
02015 }
02016 ast_variables_destroy(orig_var);
02017
02018 ast_strptime(endtime, DATE_FORMAT, &tm);
02019 now = ast_mktime(&tm, NULL);
02020
02021 now.tv_sec += extendby;
02022
02023 ast_localtime(&now, &tm, NULL);
02024 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
02025 strcat(currenttime, "0");
02026
02027 var = ast_load_realtime("meetme", "confno",
02028 confno, "startTime<= ", currenttime,
02029 "endtime>= ", currenttime, NULL);
02030
02031
02032 if (!var) {
02033 ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
02034 ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
02035 return 0;
02036
02037 }
02038
02039 ast_variables_destroy(var);
02040 return -1;
02041 }
02042
02043 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
02044 {
02045 char *original_moh;
02046
02047 ast_channel_lock(chan);
02048 original_moh = ast_strdupa(chan->musicclass);
02049 ast_string_field_set(chan, musicclass, musicclass);
02050 ast_channel_unlock(chan);
02051
02052 ast_moh_start(chan, original_moh, NULL);
02053
02054 ast_channel_lock(chan);
02055 ast_string_field_set(chan, musicclass, original_moh);
02056 ast_channel_unlock(chan);
02057 }
02058
02059 static const char *get_announce_filename(enum announcetypes type)
02060 {
02061 switch (type) {
02062 case CONF_HASLEFT:
02063 return "conf-hasleft";
02064 break;
02065 case CONF_HASJOIN:
02066 return "conf-hasjoin";
02067 break;
02068 default:
02069 return "";
02070 }
02071 }
02072
02073 static void *announce_thread(void *data)
02074 {
02075 struct announce_listitem *current;
02076 struct ast_conference *conf = data;
02077 int res;
02078 char filename[PATH_MAX] = "";
02079 AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
02080 AST_LIST_HEAD_INIT_NOLOCK(&local_list);
02081
02082 while (!conf->announcethread_stop) {
02083 ast_mutex_lock(&conf->announcelistlock);
02084 if (conf->announcethread_stop) {
02085 ast_mutex_unlock(&conf->announcelistlock);
02086 break;
02087 }
02088 if (AST_LIST_EMPTY(&conf->announcelist))
02089 ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
02090
02091 AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
02092 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
02093
02094 ast_mutex_unlock(&conf->announcelistlock);
02095 if (conf->announcethread_stop) {
02096 break;
02097 }
02098
02099 for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
02100 ast_log(LOG_DEBUG, "About to play %s\n", current->namerecloc);
02101 if (!ast_fileexists(current->namerecloc, NULL, NULL))
02102 continue;
02103 if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
02104 if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
02105 res = ast_waitstream(current->confchan, "");
02106 if (!res) {
02107 ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
02108 if (!ast_streamfile(current->confchan, filename, current->language))
02109 ast_waitstream(current->confchan, "");
02110 }
02111 }
02112 if (current->announcetype == CONF_HASLEFT) {
02113 ast_filedelete(current->namerecloc, NULL);
02114 }
02115 }
02116 }
02117
02118
02119 while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
02120 ast_filedelete(current->namerecloc, NULL);
02121 ao2_ref(current, -1);
02122 }
02123 return NULL;
02124 }
02125
02126 static int can_write(struct ast_channel *chan, struct ast_flags64 *confflags)
02127 {
02128 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
02129 return 1;
02130 }
02131
02132 return (chan->_state == AST_STATE_UP);
02133 }
02134
02135 static void send_talking_event(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking)
02136 {
02137 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalking",
02138 "Channel: %s\r\n"
02139 "Uniqueid: %s\r\n"
02140 "Meetme: %s\r\n"
02141 "Usernum: %d\r\n"
02142 "Status: %s\r\n",
02143 chan->name, chan->uniqueid, conf->confno, user->user_no, talking ? "on" : "off");
02144 }
02145
02146 static void set_user_talking(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor)
02147 {
02148 int last_talking = user->talking;
02149 if (last_talking == talking)
02150 return;
02151
02152 user->talking = talking;
02153
02154 if (monitor) {
02155
02156 int was_talking = (last_talking > 0);
02157 int now_talking = (talking > 0);
02158 if (was_talking != now_talking) {
02159 send_talking_event(chan, conf, user, now_talking);
02160 }
02161 }
02162 }
02163
02164 static int user_set_kickme_cb(void *obj, void *check_admin_arg, int flags)
02165 {
02166 struct ast_conf_user *user = obj;
02167
02168
02169 if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
02170 user->adminflags |= ADMINFLAG_KICKME;
02171 }
02172 return 0;
02173 }
02174
02175 static int user_set_unmuted_cb(void *obj, void *check_admin_arg, int flags)
02176 {
02177 struct ast_conf_user *user = obj;
02178
02179
02180 if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
02181 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
02182 }
02183 return 0;
02184 }
02185
02186 static int user_set_muted_cb(void *obj, void *check_admin_arg, int flags)
02187 {
02188 struct ast_conf_user *user = obj;
02189
02190
02191 if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
02192 user->adminflags |= ADMINFLAG_MUTED;
02193 }
02194 return 0;
02195 }
02196
02197 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struct ast_flags64 *confflags, char *optargs[])
02198 {
02199 struct ast_conf_user *user = NULL;
02200 int fd;
02201 struct dahdi_confinfo dahdic, dahdic_empty;
02202 struct ast_frame *f;
02203 struct ast_channel *c;
02204 struct ast_frame fr;
02205 int outfd;
02206 int ms;
02207 int nfds;
02208 int res;
02209 int retrydahdi;
02210 int origfd;
02211 int musiconhold = 0, mohtempstopped = 0;
02212 int firstpass = 0;
02213 int lastmarked = 0;
02214 int currentmarked = 0;
02215 int ret = -1;
02216 int x;
02217 int menu_active = 0;
02218 int menu8_active = 0;
02219 int talkreq_manager = 0;
02220 int using_pseudo = 0;
02221 int duration = 20;
02222 int sent_event = 0;
02223 int checked = 0;
02224 int announcement_played = 0;
02225 struct timeval now;
02226 struct ast_dsp *dsp = NULL;
02227 struct ast_app *agi_app;
02228 char *agifile, *mod_speex;
02229 const char *agifiledefault = "conf-background.agi", *tmpvar;
02230 char meetmesecs[30] = "";
02231 char exitcontext[AST_MAX_CONTEXT] = "";
02232 char recordingtmp[AST_MAX_EXTENSION] = "";
02233 char members[10] = "";
02234 int dtmf, opt_waitmarked_timeout = 0;
02235 time_t timeout = 0;
02236 struct dahdi_bufferinfo bi;
02237 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
02238 char *buf = __buf + AST_FRIENDLY_OFFSET;
02239 char *exitkeys = NULL;
02240 unsigned int calldurationlimit = 0;
02241 long timelimit = 0;
02242 long play_warning = 0;
02243 long warning_freq = 0;
02244 const char *warning_sound = NULL;
02245 const char *end_sound = NULL;
02246 char *parse;
02247 long time_left_ms = 0;
02248 struct timeval nexteventts = { 0, };
02249 int to;
02250 int setusercount = 0;
02251 int confsilence = 0, totalsilence = 0;
02252
02253 if (!(user = ao2_alloc(sizeof(*user), NULL))) {
02254 return ret;
02255 }
02256
02257
02258 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
02259 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
02260 (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
02261 (opt_waitmarked_timeout > 0)) {
02262 timeout = time(NULL) + opt_waitmarked_timeout;
02263 }
02264
02265 if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
02266 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
02267 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
02268 }
02269
02270 if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
02271 char *limit_str, *warning_str, *warnfreq_str;
02272 const char *var;
02273
02274 parse = optargs[OPT_ARG_DURATION_LIMIT];
02275 limit_str = strsep(&parse, ":");
02276 warning_str = strsep(&parse, ":");
02277 warnfreq_str = parse;
02278
02279 timelimit = atol(limit_str);
02280 if (warning_str)
02281 play_warning = atol(warning_str);
02282 if (warnfreq_str)
02283 warning_freq = atol(warnfreq_str);
02284
02285 if (!timelimit) {
02286 timelimit = play_warning = warning_freq = 0;
02287 warning_sound = NULL;
02288 } else if (play_warning > timelimit) {
02289 if (!warning_freq) {
02290 play_warning = 0;
02291 } else {
02292 while (play_warning > timelimit)
02293 play_warning -= warning_freq;
02294 if (play_warning < 1)
02295 play_warning = warning_freq = 0;
02296 }
02297 }
02298
02299 ast_verb(3, "Setting conference duration limit to: %ldms.\n", timelimit);
02300 if (play_warning) {
02301 ast_verb(3, "Setting warning time to %ldms from the conference duration limit.\n", play_warning);
02302 }
02303 if (warning_freq) {
02304 ast_verb(3, "Setting warning frequency to %ldms.\n", warning_freq);
02305 }
02306
02307 ast_channel_lock(chan);
02308 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
02309 var = ast_strdupa(var);
02310 }
02311 ast_channel_unlock(chan);
02312
02313 warning_sound = var ? var : "timeleft";
02314
02315 ast_channel_lock(chan);
02316 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
02317 var = ast_strdupa(var);
02318 }
02319 ast_channel_unlock(chan);
02320
02321 end_sound = var ? var : NULL;
02322
02323
02324 calldurationlimit = 0;
02325
02326 if (!play_warning && !end_sound && timelimit) {
02327 calldurationlimit = timelimit / 1000;
02328 timelimit = play_warning = warning_freq = 0;
02329 } else {
02330 ast_debug(2, "Limit Data for this call:\n");
02331 ast_debug(2, "- timelimit = %ld\n", timelimit);
02332 ast_debug(2, "- play_warning = %ld\n", play_warning);
02333 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
02334 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
02335 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
02336 }
02337 }
02338
02339
02340 if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
02341 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
02342 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
02343 else
02344 exitkeys = ast_strdupa("#");
02345 }
02346
02347 if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
02348 if (!conf->recordingfilename) {
02349 const char *var;
02350 ast_channel_lock(chan);
02351 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
02352 conf->recordingfilename = ast_strdup(var);
02353 }
02354 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
02355 conf->recordingformat = ast_strdup(var);
02356 }
02357 ast_channel_unlock(chan);
02358 if (!conf->recordingfilename) {
02359 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
02360 conf->recordingfilename = ast_strdup(recordingtmp);
02361 }
02362 if (!conf->recordingformat) {
02363 conf->recordingformat = ast_strdup("wav");
02364 }
02365 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
02366 conf->confno, conf->recordingfilename, conf->recordingformat);
02367 }
02368 }
02369
02370 ast_mutex_lock(&conf->recordthreadlock);
02371 if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
02372 ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, chan, "pseudo", NULL)))) {
02373 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
02374 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
02375 dahdic.chan = 0;
02376 dahdic.confno = conf->dahdiconf;
02377 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
02378 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
02379 ast_log(LOG_WARNING, "Error starting listen channel\n");
02380 ast_hangup(conf->lchan);
02381 conf->lchan = NULL;
02382 } else {
02383 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
02384 }
02385 }
02386 ast_mutex_unlock(&conf->recordthreadlock);
02387
02388 ast_mutex_lock(&conf->announcethreadlock);
02389 if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
02390 (ast_test_flag64(confflags, CONFFLAG_INTROUSER) || ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))) {
02391 ast_mutex_init(&conf->announcelistlock);
02392 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
02393 ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
02394 }
02395 ast_mutex_unlock(&conf->announcethreadlock);
02396
02397 time(&user->jointime);
02398
02399 user->timelimit = timelimit;
02400 user->play_warning = play_warning;
02401 user->warning_freq = warning_freq;
02402 user->warning_sound = warning_sound;
02403 user->end_sound = end_sound;
02404
02405 if (calldurationlimit > 0) {
02406 time(&user->kicktime);
02407 user->kicktime = user->kicktime + calldurationlimit;
02408 }
02409
02410 if (ast_tvzero(user->start_time))
02411 user->start_time = ast_tvnow();
02412 time_left_ms = user->timelimit;
02413
02414 if (user->timelimit) {
02415 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
02416 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
02417 }
02418
02419 if (conf->locked && (!ast_test_flag64(confflags, CONFFLAG_ADMIN))) {
02420
02421 if (!ast_streamfile(chan, "conf-locked", chan->language))
02422 ast_waitstream(chan, "");
02423 goto outrun;
02424 }
02425
02426 ast_mutex_lock(&conf->playlock);
02427
02428 if (rt_schedule && conf->maxusers) {
02429 if (conf->users >= conf->maxusers) {
02430
02431 if (!ast_streamfile(chan, "conf-full", chan->language))
02432 ast_waitstream(chan, "");
02433 ast_mutex_unlock(&conf->playlock);
02434 goto outrun;
02435 }
02436 }
02437
02438 ao2_lock(conf->usercontainer);
02439 ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &user->user_no);
02440 user->user_no++;
02441 ao2_link(conf->usercontainer, user);
02442 ao2_unlock(conf->usercontainer);
02443
02444 user->chan = chan;
02445 user->userflags = *confflags;
02446 user->adminflags = ast_test_flag64(confflags, CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
02447 user->adminflags |= (conf->gmuted) ? ADMINFLAG_MUTED : 0;
02448 user->talking = -1;
02449
02450 ast_mutex_unlock(&conf->playlock);
02451
02452 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
02453 ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))) {
02454 char destdir[PATH_MAX];
02455
02456 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
02457
02458 if (ast_mkdir(destdir, 0777) != 0) {
02459 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
02460 goto outrun;
02461 }
02462
02463 snprintf(user->namerecloc, sizeof(user->namerecloc),
02464 "%s/meetme-username-%s-%d", destdir,
02465 conf->confno, user->user_no);
02466 if (ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))
02467 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
02468 else
02469 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
02470 if (res == -1)
02471 goto outrun;
02472 }
02473
02474 ast_mutex_lock(&conf->playlock);
02475
02476 if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER))
02477 conf->markedusers++;
02478 conf->users++;
02479 if (rt_log_members) {
02480
02481 snprintf(members, sizeof(members), "%d", conf->users);
02482 ast_realtime_require_field("meetme",
02483 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
02484 "members", RQ_UINTEGER1, strlen(members),
02485 NULL);
02486 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
02487 }
02488 setusercount = 1;
02489
02490
02491 if (conf->users == 1)
02492 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
02493
02494 ast_mutex_unlock(&conf->playlock);
02495
02496
02497 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
02498
02499 if (ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT)) {
02500 ast_channel_lock(chan);
02501 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
02502 ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
02503 } else if (!ast_strlen_zero(chan->macrocontext)) {
02504 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
02505 } else {
02506 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
02507 }
02508 ast_channel_unlock(chan);
02509 }
02510
02511
02512 if (ast_test_flag64(confflags, CONFFLAG_INTROMSG) &&
02513 !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
02514 if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], chan->language)) {
02515 ast_waitstream(chan, "");
02516 }
02517 }
02518
02519 if (!ast_test_flag64(confflags, (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
02520 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED))
02521 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
02522 ast_waitstream(chan, "");
02523 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && conf->markedusers == 0)
02524 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
02525 ast_waitstream(chan, "");
02526 }
02527
02528 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_ANNOUNCEUSERCOUNT) &&
02529 conf->users > 1) {
02530 int keepplaying = 1;
02531
02532 if (conf->users == 2) {
02533 if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
02534 res = ast_waitstream(chan, AST_DIGIT_ANY);
02535 ast_stopstream(chan);
02536 if (res > 0)
02537 keepplaying = 0;
02538 else if (res == -1)
02539 goto outrun;
02540 }
02541 } else {
02542 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
02543 res = ast_waitstream(chan, AST_DIGIT_ANY);
02544 ast_stopstream(chan);
02545 if (res > 0)
02546 keepplaying = 0;
02547 else if (res == -1)
02548 goto outrun;
02549 }
02550 if (keepplaying) {
02551 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
02552 if (res > 0)
02553 keepplaying = 0;
02554 else if (res == -1)
02555 goto outrun;
02556 }
02557 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
02558 res = ast_waitstream(chan, AST_DIGIT_ANY);
02559 ast_stopstream(chan);
02560 if (res > 0)
02561 keepplaying = 0;
02562 else if (res == -1)
02563 goto outrun;
02564 }
02565 }
02566 }
02567
02568 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
02569
02570 ast_indicate(chan, -1);
02571 }
02572
02573 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
02574 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
02575 goto outrun;
02576 }
02577
02578 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
02579 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
02580 goto outrun;
02581 }
02582
02583
02584 if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
02585 ast_free(mod_speex);
02586 ast_func_write(chan, "DENOISE(rx)", "on");
02587 }
02588
02589 retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
02590 user->dahdichannel = !retrydahdi;
02591
02592 dahdiretry:
02593 origfd = chan->fds[0];
02594 if (retrydahdi) {
02595
02596 fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
02597 if (fd < 0) {
02598 ast_log(LOG_WARNING, "Unable to open DAHDI pseudo channel: %s\n", strerror(errno));
02599 goto outrun;
02600 }
02601 using_pseudo = 1;
02602
02603 memset(&bi, 0, sizeof(bi));
02604 bi.bufsize = CONF_SIZE / 2;
02605 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
02606 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
02607 bi.numbufs = audio_buffers;
02608 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
02609 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
02610 close(fd);
02611 goto outrun;
02612 }
02613 x = 1;
02614 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
02615 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
02616 close(fd);
02617 goto outrun;
02618 }
02619 nfds = 1;
02620 } else {
02621
02622 fd = chan->fds[0];
02623 nfds = 0;
02624 }
02625 memset(&dahdic, 0, sizeof(dahdic));
02626 memset(&dahdic_empty, 0, sizeof(dahdic_empty));
02627
02628 dahdic.chan = 0;
02629 if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
02630 ast_log(LOG_WARNING, "Error getting conference\n");
02631 close(fd);
02632 goto outrun;
02633 }
02634 if (dahdic.confmode) {
02635
02636 if (!retrydahdi) {
02637 ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
02638 retrydahdi = 1;
02639 goto dahdiretry;
02640 }
02641 }
02642 memset(&dahdic, 0, sizeof(dahdic));
02643
02644 dahdic.chan = 0;
02645 dahdic.confno = conf->dahdiconf;
02646
02647 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
02648 ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
02649 struct announce_listitem *item;
02650 if (!(item = ao2_alloc(sizeof(*item), NULL)))
02651 goto outrun;
02652 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
02653 ast_copy_string(item->language, chan->language, sizeof(item->language));
02654 item->confchan = conf->chan;
02655 item->confusers = conf->users;
02656 item->announcetype = CONF_HASJOIN;
02657 ast_mutex_lock(&conf->announcelistlock);
02658 ao2_ref(item, +1);
02659 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
02660 ast_cond_signal(&conf->announcelist_addition);
02661 ast_mutex_unlock(&conf->announcelistlock);
02662
02663 while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
02664 ;
02665 }
02666 ao2_ref(item, -1);
02667 }
02668
02669 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && !conf->markedusers)
02670 dahdic.confmode = DAHDI_CONF_CONF;
02671 else if (ast_test_flag64(confflags, CONFFLAG_MONITOR))
02672 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
02673 else if (ast_test_flag64(confflags, CONFFLAG_TALKER))
02674 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
02675 else
02676 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
02677
02678 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
02679 ast_log(LOG_WARNING, "Error setting conference\n");
02680 close(fd);
02681 goto outrun;
02682 }
02683 ast_debug(1, "Placed channel %s in DAHDI conf %d\n", chan->name, conf->dahdiconf);
02684
02685 if (!sent_event) {
02686 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeJoin",
02687 "Channel: %s\r\n"
02688 "Uniqueid: %s\r\n"
02689 "Meetme: %s\r\n"
02690 "Usernum: %d\r\n"
02691 "CallerIDnum: %s\r\n"
02692 "CallerIDname: %s\r\n"
02693 "ConnectedLineNum: %s\r\n"
02694 "ConnectedLineName: %s\r\n",
02695 chan->name, chan->uniqueid, conf->confno,
02696 user->user_no,
02697 S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
02698 S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<unknown>"),
02699 S_COR(user->chan->connected.id.number.valid, user->chan->connected.id.number.str, "<unknown>"),
02700 S_COR(user->chan->connected.id.name.valid, user->chan->connected.id.name.str, "<unknown>")
02701 );
02702 sent_event = 1;
02703 }
02704
02705 if (!firstpass && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
02706 !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
02707 firstpass = 1;
02708 if (!ast_test_flag64(confflags, CONFFLAG_QUIET))
02709 if (!ast_test_flag64(confflags, CONFFLAG_WAITMARKED) || (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
02710 (conf->markedusers >= 1))) {
02711 conf_play(chan, conf, ENTER);
02712 }
02713 }
02714
02715 conf_flush(fd, chan);
02716
02717 if (dsp)
02718 ast_dsp_free(dsp);
02719
02720 if (!(dsp = ast_dsp_new())) {
02721 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
02722 res = -1;
02723 }
02724
02725 if (ast_test_flag64(confflags, CONFFLAG_AGI)) {
02726
02727
02728
02729 ast_channel_lock(chan);
02730 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
02731 agifile = ast_strdupa(tmpvar);
02732 } else {
02733 agifile = ast_strdupa(agifiledefault);
02734 }
02735 ast_channel_unlock(chan);
02736
02737 if (user->dahdichannel) {
02738
02739 x = 1;
02740 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
02741 }
02742
02743 agi_app = pbx_findapp("agi");
02744 if (agi_app) {
02745 ret = pbx_exec(chan, agi_app, agifile);
02746 } else {
02747 ast_log(LOG_WARNING, "Could not find application (agi)\n");
02748 ret = -2;
02749 }
02750 if (user->dahdichannel) {
02751
02752 x = 0;
02753 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
02754 }
02755 } else {
02756 if (user->dahdichannel && ast_test_flag64(confflags, CONFFLAG_STARMENU)) {
02757
02758 x = 1;
02759 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
02760 }
02761 for (;;) {
02762 int menu_was_active = 0;
02763
02764 outfd = -1;
02765 ms = -1;
02766 now = ast_tvnow();
02767
02768 if (rt_schedule && conf->endtime) {
02769 char currenttime[32];
02770 long localendtime = 0;
02771 int extended = 0;
02772 struct ast_tm tm;
02773 struct ast_variable *var, *origvar;
02774 struct timeval tmp;
02775
02776 if (now.tv_sec % 60 == 0) {
02777 if (!checked) {
02778 ast_localtime(&now, &tm, NULL);
02779 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
02780 var = origvar = ast_load_realtime("meetme", "confno",
02781 conf->confno, "starttime <=", currenttime,
02782 "endtime >=", currenttime, NULL);
02783
02784 for ( ; var; var = var->next) {
02785 if (!strcasecmp(var->name, "endtime")) {
02786 struct ast_tm endtime_tm;
02787 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
02788 tmp = ast_mktime(&endtime_tm, NULL);
02789 localendtime = tmp.tv_sec;
02790 }
02791 }
02792 ast_variables_destroy(origvar);
02793
02794
02795
02796 if (localendtime > conf->endtime){
02797 conf->endtime = localendtime;
02798 extended = 1;
02799 }
02800
02801 if (conf->endtime && (now.tv_sec >= conf->endtime)) {
02802 ast_verbose("Quitting time...\n");
02803 goto outrun;
02804 }
02805
02806 if (!announcement_played && conf->endalert) {
02807 if (now.tv_sec + conf->endalert >= conf->endtime) {
02808 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
02809 ast_waitstream(chan, "");
02810 ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", chan->language);
02811 if (!ast_streamfile(chan, "minutes", chan->language))
02812 ast_waitstream(chan, "");
02813 if (musiconhold) {
02814 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
02815 }
02816 announcement_played = 1;
02817 }
02818 }
02819
02820 if (extended) {
02821 announcement_played = 0;
02822 }
02823
02824 checked = 1;
02825 }
02826 } else {
02827 checked = 0;
02828 }
02829 }
02830
02831 if (user->kicktime && (user->kicktime <= now.tv_sec)) {
02832 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
02833 ret = 0;
02834 } else {
02835 ret = -1;
02836 }
02837 break;
02838 }
02839
02840 to = -1;
02841 if (user->timelimit) {
02842 int minutes = 0, seconds = 0, remain = 0;
02843
02844 to = ast_tvdiff_ms(nexteventts, now);
02845 if (to < 0) {
02846 to = 0;
02847 }
02848 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
02849 if (time_left_ms < to) {
02850 to = time_left_ms;
02851 }
02852
02853 if (time_left_ms <= 0) {
02854 if (user->end_sound) {
02855 res = ast_streamfile(chan, user->end_sound, chan->language);
02856 res = ast_waitstream(chan, "");
02857 }
02858 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
02859 ret = 0;
02860 } else {
02861 ret = -1;
02862 }
02863 break;
02864 }
02865
02866 if (!to) {
02867 if (time_left_ms >= 5000) {
02868
02869 remain = (time_left_ms + 500) / 1000;
02870 if (remain / 60 >= 1) {
02871 minutes = remain / 60;
02872 seconds = remain % 60;
02873 } else {
02874 seconds = remain;
02875 }
02876
02877
02878 if (user->warning_sound && user->play_warning) {
02879 if (!strcmp(user->warning_sound, "timeleft")) {
02880
02881 res = ast_streamfile(chan, "vm-youhave", chan->language);
02882 res = ast_waitstream(chan, "");
02883 if (minutes) {
02884 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
02885 res = ast_streamfile(chan, "queue-minutes", chan->language);
02886 res = ast_waitstream(chan, "");
02887 }
02888 if (seconds) {
02889 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
02890 res = ast_streamfile(chan, "queue-seconds", chan->language);
02891 res = ast_waitstream(chan, "");
02892 }
02893 } else {
02894 res = ast_streamfile(chan, user->warning_sound, chan->language);
02895 res = ast_waitstream(chan, "");
02896 }
02897 if (musiconhold) {
02898 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
02899 }
02900 }
02901 }
02902 if (user->warning_freq) {
02903 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
02904 } else {
02905 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
02906 }
02907 }
02908 }
02909
02910 now = ast_tvnow();
02911 if (timeout && now.tv_sec >= timeout) {
02912 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
02913 ret = 0;
02914 } else {
02915 ret = -1;
02916 }
02917 break;
02918 }
02919
02920
02921
02922
02923 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual) {
02924 set_talk_volume(user, user->listen.desired);
02925 }
02926
02927 menu_was_active = menu_active;
02928
02929 currentmarked = conf->markedusers;
02930 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
02931 ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
02932 ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
02933 lastmarked == 0) {
02934 if (currentmarked == 1 && conf->users > 1) {
02935 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
02936 if (conf->users - 1 == 1) {
02937 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language)) {
02938 ast_waitstream(chan, "");
02939 }
02940 } else {
02941 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language)) {
02942 ast_waitstream(chan, "");
02943 }
02944 }
02945 }
02946 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
02947 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
02948 ast_waitstream(chan, "");
02949 }
02950 }
02951 }
02952
02953
02954 user->userflags = *confflags;
02955
02956 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
02957 if (currentmarked == 0) {
02958 if (lastmarked != 0) {
02959 if (!ast_test_flag64(confflags, CONFFLAG_QUIET)) {
02960 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language)) {
02961 ast_waitstream(chan, "");
02962 }
02963 }
02964 if (ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
02965 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
02966 ret = 0;
02967 }
02968 break;
02969 } else {
02970 dahdic.confmode = DAHDI_CONF_CONF;
02971 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
02972 ast_log(LOG_WARNING, "Error setting conference\n");
02973 close(fd);
02974 goto outrun;
02975 }
02976 }
02977 }
02978 if (!musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
02979 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
02980 musiconhold = 1;
02981 }
02982 } else if (currentmarked >= 1 && lastmarked == 0) {
02983
02984 timeout = 0;
02985 if (ast_test_flag64(confflags, CONFFLAG_MONITOR)) {
02986 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
02987 } else if (ast_test_flag64(confflags, CONFFLAG_TALKER)) {
02988 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
02989 } else {
02990 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
02991 }
02992 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
02993 ast_log(LOG_WARNING, "Error setting conference\n");
02994 close(fd);
02995 goto outrun;
02996 }
02997 if (musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
02998 ast_moh_stop(chan);
02999 musiconhold = 0;
03000 }
03001 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03002 !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
03003 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language)) {
03004 ast_waitstream(chan, "");
03005 }
03006 conf_play(chan, conf, ENTER);
03007 }
03008 }
03009 }
03010
03011
03012 if (ast_test_flag64(confflags, CONFFLAG_MOH) && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
03013 if (conf->users == 1) {
03014 if (!musiconhold) {
03015 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03016 musiconhold = 1;
03017 }
03018 } else {
03019 if (musiconhold) {
03020 ast_moh_stop(chan);
03021 musiconhold = 0;
03022 }
03023 }
03024 }
03025
03026
03027 if (currentmarked == 0 && lastmarked != 0 && ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
03028 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03029 ret = 0;
03030 } else {
03031 ret = -1;
03032 }
03033 break;
03034 }
03035
03036
03037
03038
03039 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
03040 dahdic.confmode ^= DAHDI_CONF_TALKER;
03041 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03042 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
03043 ret = -1;
03044 break;
03045 }
03046
03047
03048 if (ast_test_flag64(confflags, (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER))) {
03049 set_user_talking(chan, conf, user, -1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03050 }
03051
03052 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
03053 "Channel: %s\r\n"
03054 "Uniqueid: %s\r\n"
03055 "Meetme: %s\r\n"
03056 "Usernum: %d\r\n"
03057 "Status: on\r\n",
03058 chan->name, chan->uniqueid, conf->confno, user->user_no);
03059 }
03060
03061
03062 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
03063 dahdic.confmode |= DAHDI_CONF_TALKER;
03064 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03065 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
03066 ret = -1;
03067 break;
03068 }
03069
03070 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
03071 "Channel: %s\r\n"
03072 "Uniqueid: %s\r\n"
03073 "Meetme: %s\r\n"
03074 "Usernum: %d\r\n"
03075 "Status: off\r\n",
03076 chan->name, chan->uniqueid, conf->confno, user->user_no);
03077 }
03078
03079 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
03080 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
03081 talkreq_manager = 1;
03082
03083 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
03084 "Channel: %s\r\n"
03085 "Uniqueid: %s\r\n"
03086 "Meetme: %s\r\n"
03087 "Usernum: %d\r\n"
03088 "Status: on\r\n",
03089 chan->name, chan->uniqueid, conf->confno, user->user_no);
03090 }
03091
03092
03093 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
03094 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
03095 talkreq_manager = 0;
03096 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
03097 "Channel: %s\r\n"
03098 "Uniqueid: %s\r\n"
03099 "Meetme: %s\r\n"
03100 "Usernum: %d\r\n"
03101 "Status: off\r\n",
03102 chan->name, chan->uniqueid, conf->confno, user->user_no);
03103 }
03104
03105
03106 if (user->adminflags & ADMINFLAG_KICKME) {
03107
03108 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03109 !ast_streamfile(chan, "conf-kicked", chan->language)) {
03110 ast_waitstream(chan, "");
03111 }
03112 ret = 0;
03113 break;
03114 }
03115
03116 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
03117
03118 if (c) {
03119 char dtmfstr[2] = "";
03120
03121 if (c->fds[0] != origfd || (user->dahdichannel && (c->audiohooks || c->monitor))) {
03122 if (using_pseudo) {
03123
03124 close(fd);
03125 using_pseudo = 0;
03126 }
03127 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
03128 retrydahdi = (strcasecmp(c->tech->type, "DAHDI") || (c->audiohooks || c->monitor) ? 1 : 0);
03129 user->dahdichannel = !retrydahdi;
03130 goto dahdiretry;
03131 }
03132 if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
03133 f = ast_read_noaudio(c);
03134 } else {
03135 f = ast_read(c);
03136 }
03137 if (!f) {
03138 break;
03139 }
03140 if (f->frametype == AST_FRAME_DTMF) {
03141 dtmfstr[0] = f->subclass.integer;
03142 dtmfstr[1] = '\0';
03143 }
03144
03145 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.codec == AST_FORMAT_SLINEAR)) {
03146 if (user->talk.actual) {
03147 ast_frame_adjust_volume(f, user->talk.actual);
03148 }
03149
03150 if (ast_test_flag64(confflags, (CONFFLAG_OPTIMIZETALKER | CONFFLAG_MONITORTALKER))) {
03151 if (user->talking == -1) {
03152 user->talking = 0;
03153 }
03154
03155 res = ast_dsp_silence(dsp, f, &totalsilence);
03156 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
03157 set_user_talking(chan, conf, user, 1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03158 }
03159
03160 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
03161 set_user_talking(chan, conf, user, 0, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03162 }
03163 }
03164 if (using_pseudo) {
03165
03166
03167
03168
03169
03170
03171
03172
03173
03174
03175
03176
03177 if (user->talking || !ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER)) {
03178 careful_write(fd, f->data.ptr, f->datalen, 0);
03179 }
03180 }
03181 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass.integer == '*') && ast_test_flag64(confflags, CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
03182 if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
03183 conf_queue_dtmf(conf, user, f);
03184 }
03185 if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
03186 ast_log(LOG_WARNING, "Error setting conference\n");
03187 close(fd);
03188 ast_frfree(f);
03189 goto outrun;
03190 }
03191
03192
03193
03194
03195 if (!menu_active && user->talk.desired && !user->talk.actual) {
03196 set_talk_volume(user, 0);
03197 }
03198
03199 if (musiconhold) {
03200 ast_moh_stop(chan);
03201 }
03202 if (menu8_active) {
03203
03204 dtmf = f->subclass.integer;
03205 if (dtmf) {
03206 int keepplaying;
03207 int playednamerec;
03208 struct ao2_iterator user_iter;
03209 struct ast_conf_user *usr = NULL;
03210 switch(dtmf) {
03211 case '1':
03212 keepplaying = 1;
03213 playednamerec = 0;
03214 if (conf->users == 1) {
03215 if (keepplaying && !ast_streamfile(chan, "conf-onlyperson", chan->language)) {
03216 res = ast_waitstream(chan, AST_DIGIT_ANY);
03217 ast_stopstream(chan);
03218 if (res > 0)
03219 keepplaying = 0;
03220 }
03221 } else if (conf->users == 2) {
03222 if (keepplaying && !ast_streamfile(chan, "conf-onlyone", chan->language)) {
03223 res = ast_waitstream(chan, AST_DIGIT_ANY);
03224 ast_stopstream(chan);
03225 if (res > 0)
03226 keepplaying = 0;
03227 }
03228 } else {
03229 if (keepplaying && !ast_streamfile(chan, "conf-thereare", chan->language)) {
03230 res = ast_waitstream(chan, AST_DIGIT_ANY);
03231 ast_stopstream(chan);
03232 if (res > 0)
03233 keepplaying = 0;
03234 }
03235 if (keepplaying) {
03236 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
03237 if (res > 0)
03238 keepplaying = 0;
03239 }
03240 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
03241 res = ast_waitstream(chan, AST_DIGIT_ANY);
03242 ast_stopstream(chan);
03243 if (res > 0)
03244 keepplaying = 0;
03245 }
03246 }
03247 user_iter = ao2_iterator_init(conf->usercontainer, 0);
03248 while((usr = ao2_iterator_next(&user_iter))) {
03249 if (ast_fileexists(usr->namerecloc, NULL, NULL)) {
03250 if (keepplaying && !ast_streamfile(chan, usr->namerecloc, chan->language)) {
03251 res = ast_waitstream(chan, AST_DIGIT_ANY);
03252 ast_stopstream(chan);
03253 if (res > 0)
03254 keepplaying = 0;
03255 }
03256 playednamerec = 1;
03257 }
03258 ao2_ref(usr, -1);
03259 }
03260 ao2_iterator_destroy(&user_iter);
03261 if (keepplaying && playednamerec && !ast_streamfile(chan, "conf-roll-callcomplete", chan->language)) {
03262 res = ast_waitstream(chan, AST_DIGIT_ANY);
03263 ast_stopstream(chan);
03264 if (res > 0)
03265 keepplaying = 0;
03266 }
03267 break;
03268 case '2':
03269 if (conf->users == 1) {
03270 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
03271 ast_waitstream(chan, "");
03272 } else {
03273 ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_kickme_cb, &conf);
03274 }
03275 ast_stopstream(chan);
03276 break;
03277 case '3':
03278 if(conf->gmuted) {
03279 conf->gmuted = 0;
03280 ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, &conf);
03281 if (!ast_streamfile(chan, "conf-now-unmuted", chan->language))
03282 ast_waitstream(chan, "");
03283 } else {
03284 conf->gmuted = 1;
03285 ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_muted_cb, &conf);
03286 if (!ast_streamfile(chan, "conf-now-muted", chan->language))
03287 ast_waitstream(chan, "");
03288 }
03289 ast_stopstream(chan);
03290 break;
03291 case '4':
03292 if (conf->recording != MEETME_RECORD_ACTIVE) {
03293 ast_set_flag64(confflags, CONFFLAG_RECORDCONF);
03294
03295 if (!conf->recordingfilename) {
03296 const char *var;
03297 ast_channel_lock(chan);
03298 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
03299 conf->recordingfilename = ast_strdup(var);
03300 }
03301 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
03302 conf->recordingformat = ast_strdup(var);
03303 }
03304 ast_channel_unlock(chan);
03305 if (!conf->recordingfilename) {
03306 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
03307 conf->recordingfilename = ast_strdup(recordingtmp);
03308 }
03309 if (!conf->recordingformat) {
03310 conf->recordingformat = ast_strdup("wav");
03311 }
03312 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
03313 conf->confno, conf->recordingfilename, conf->recordingformat);
03314 }
03315
03316 ast_mutex_lock(&conf->recordthreadlock);
03317 if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, chan, "pseudo", NULL)))) {
03318 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
03319 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
03320 dahdic.chan = 0;
03321 dahdic.confno = conf->dahdiconf;
03322 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
03323 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
03324 ast_log(LOG_WARNING, "Error starting listen channel\n");
03325 ast_hangup(conf->lchan);
03326 conf->lchan = NULL;
03327 } else {
03328 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
03329 }
03330 }
03331 ast_mutex_unlock(&conf->recordthreadlock);
03332
03333 if (!ast_streamfile(chan, "conf-now-recording", chan->language))
03334 ast_waitstream(chan, "");
03335
03336 }
03337
03338 ast_stopstream(chan);
03339 break;
03340 default:
03341 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
03342 ast_waitstream(chan, "");
03343 ast_stopstream(chan);
03344 break;
03345 }
03346 }
03347
03348 menu8_active = 0;
03349 menu_active = 0;
03350 } else if (ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
03351
03352 if (!menu_active) {
03353 menu_active = 1;
03354
03355 if (!ast_streamfile(chan, "conf-adminmenu-162", chan->language)) {
03356 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
03357 ast_stopstream(chan);
03358 } else {
03359 dtmf = 0;
03360 }
03361 } else {
03362 dtmf = f->subclass.integer;
03363 }
03364 if (dtmf) {
03365 switch(dtmf) {
03366 case '1':
03367 menu_active = 0;
03368
03369
03370 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
03371 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
03372 } else {
03373 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
03374 }
03375
03376 if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
03377 if (!ast_streamfile(chan, "conf-muted", chan->language)) {
03378 ast_waitstream(chan, "");
03379 }
03380 } else {
03381 if (!ast_streamfile(chan, "conf-unmuted", chan->language)) {
03382 ast_waitstream(chan, "");
03383 }
03384 }
03385 break;
03386 case '2':
03387 menu_active = 0;
03388 if (conf->locked) {
03389 conf->locked = 0;
03390 if (!ast_streamfile(chan, "conf-unlockednow", chan->language)) {
03391 ast_waitstream(chan, "");
03392 }
03393 } else {
03394 conf->locked = 1;
03395 if (!ast_streamfile(chan, "conf-lockednow", chan->language)) {
03396 ast_waitstream(chan, "");
03397 }
03398 }
03399 break;
03400 case '3':
03401 {
03402 struct ast_conf_user *usr = NULL;
03403 int max_no = 0;
03404 ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
03405 menu_active = 0;
03406 usr = ao2_find(conf->usercontainer, &max_no, 0);
03407 if ((usr->chan->name == chan->name) || ast_test_flag64(&usr->userflags, CONFFLAG_ADMIN)) {
03408 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
03409 ast_waitstream(chan, "");
03410 }
03411 } else {
03412 usr->adminflags |= ADMINFLAG_KICKME;
03413 }
03414 ao2_ref(usr, -1);
03415 ast_stopstream(chan);
03416 break;
03417 }
03418 case '4':
03419 tweak_listen_volume(user, VOL_DOWN);
03420 break;
03421 case '5':
03422
03423 if (rt_schedule) {
03424 if (!rt_extend_conf(conf->confno)) {
03425 if (!ast_streamfile(chan, "conf-extended", chan->language)) {
03426 ast_waitstream(chan, "");
03427 }
03428 } else {
03429 if (!ast_streamfile(chan, "conf-nonextended", chan->language)) {
03430 ast_waitstream(chan, "");
03431 }
03432 }
03433 ast_stopstream(chan);
03434 }
03435 menu_active = 0;
03436 break;
03437 case '6':
03438 tweak_listen_volume(user, VOL_UP);
03439 break;
03440 case '7':
03441 tweak_talk_volume(user, VOL_DOWN);
03442 break;
03443 case '8':
03444 menu8_active = 1;
03445 break;
03446 case '9':
03447 tweak_talk_volume(user, VOL_UP);
03448 break;
03449 default:
03450 menu_active = 0;
03451
03452 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
03453 ast_waitstream(chan, "");
03454 }
03455 break;
03456 }
03457 }
03458 } else {
03459
03460 if (!menu_active) {
03461 menu_active = 1;
03462 if (!ast_streamfile(chan, "conf-usermenu-162", chan->language)) {
03463 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
03464 ast_stopstream(chan);
03465 } else {
03466 dtmf = 0;
03467 }
03468 } else {
03469 dtmf = f->subclass.integer;
03470 }
03471 if (dtmf) {
03472 switch (dtmf) {
03473 case '1':
03474 menu_active = 0;
03475
03476
03477 user->adminflags ^= ADMINFLAG_SELFMUTED;
03478
03479
03480 if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
03481 if (!ast_streamfile(chan, "conf-muted", chan->language)) {
03482 ast_waitstream(chan, "");
03483 }
03484 } else {
03485 if (!ast_streamfile(chan, "conf-unmuted", chan->language)) {
03486 ast_waitstream(chan, "");
03487 }
03488 }
03489 break;
03490 case '2':
03491 menu_active = 0;
03492 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
03493 user->adminflags |= ADMINFLAG_T_REQUEST;
03494 }
03495
03496 if (user->adminflags & ADMINFLAG_T_REQUEST) {
03497 if (!ast_streamfile(chan, "beep", chan->language)) {
03498 ast_waitstream(chan, "");
03499 }
03500 }
03501 break;
03502 case '4':
03503 tweak_listen_volume(user, VOL_DOWN);
03504 break;
03505 case '5':
03506
03507 if (rt_schedule) {
03508 rt_extend_conf(conf->confno);
03509 }
03510 menu_active = 0;
03511 break;
03512 case '6':
03513 tweak_listen_volume(user, VOL_UP);
03514 break;
03515 case '7':
03516 tweak_talk_volume(user, VOL_DOWN);
03517 break;
03518 case '8':
03519 menu_active = 0;
03520 break;
03521 case '9':
03522 tweak_talk_volume(user, VOL_UP);
03523 break;
03524 default:
03525 menu_active = 0;
03526 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
03527 ast_waitstream(chan, "");
03528 }
03529 break;
03530 }
03531 }
03532 }
03533 if (musiconhold && !menu_active) {
03534 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03535 }
03536
03537 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03538 ast_log(LOG_WARNING, "Error setting conference\n");
03539 close(fd);
03540 ast_frfree(f);
03541 goto outrun;
03542 }
03543
03544 conf_flush(fd, chan);
03545
03546
03547
03548
03549 } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
03550 if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
03551 conf_queue_dtmf(conf, user, f);
03552 }
03553
03554 if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
03555 ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
03556 ret = 0;
03557 ast_frfree(f);
03558 break;
03559 } else {
03560 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
03561 }
03562 } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_KEYEXIT) &&
03563 (strchr(exitkeys, f->subclass.integer))) {
03564 pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);
03565
03566 if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
03567 conf_queue_dtmf(conf, user, f);
03568 }
03569 ret = 0;
03570 ast_frfree(f);
03571 break;
03572 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
03573 && ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
03574 conf_queue_dtmf(conf, user, f);
03575 } else if (ast_test_flag64(confflags, CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
03576 switch (f->subclass.integer) {
03577 case AST_CONTROL_HOLD:
03578 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
03579 break;
03580 default:
03581 break;
03582 }
03583 } else if (f->frametype == AST_FRAME_NULL) {
03584
03585 } else if (f->frametype == AST_FRAME_CONTROL) {
03586 switch (f->subclass.integer) {
03587 case AST_CONTROL_BUSY:
03588 case AST_CONTROL_CONGESTION:
03589 ast_frfree(f);
03590 goto outrun;
03591 break;
03592 default:
03593 ast_debug(1,
03594 "Got ignored control frame on channel %s, f->frametype=%d,f->subclass=%d\n",
03595 chan->name, f->frametype, f->subclass.integer);
03596 }
03597 } else {
03598 ast_debug(1,
03599 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
03600 chan->name, f->frametype, f->subclass.integer);
03601 }
03602 ast_frfree(f);
03603 } else if (outfd > -1) {
03604 res = read(outfd, buf, CONF_SIZE);
03605 if (res > 0) {
03606 memset(&fr, 0, sizeof(fr));
03607 fr.frametype = AST_FRAME_VOICE;
03608 fr.subclass.codec = AST_FORMAT_SLINEAR;
03609 fr.datalen = res;
03610 fr.samples = res / 2;
03611 fr.data.ptr = buf;
03612 fr.offset = AST_FRIENDLY_OFFSET;
03613 if (!user->listen.actual &&
03614 (ast_test_flag64(confflags, CONFFLAG_MONITOR) ||
03615 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
03616 (!user->talking && ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER))
03617 )) {
03618 int idx;
03619 for (idx = 0; idx < AST_FRAME_BITS; idx++) {
03620 if (chan->rawwriteformat & (1 << idx)) {
03621 break;
03622 }
03623 }
03624 if (idx >= AST_FRAME_BITS) {
03625 goto bailoutandtrynormal;
03626 }
03627 ast_mutex_lock(&conf->listenlock);
03628 if (!conf->transframe[idx]) {
03629 if (conf->origframe) {
03630 if (musiconhold && !ast_dsp_silence(dsp, conf->origframe, &confsilence) && confsilence < MEETME_DELAYDETECTTALK) {
03631 ast_moh_stop(chan);
03632 mohtempstopped = 1;
03633 }
03634 if (!conf->transpath[idx]) {
03635 conf->transpath[idx] = ast_translator_build_path((1 << idx), AST_FORMAT_SLINEAR);
03636 }
03637 if (conf->transpath[idx]) {
03638 conf->transframe[idx] = ast_translate(conf->transpath[idx], conf->origframe, 0);
03639 if (!conf->transframe[idx]) {
03640 conf->transframe[idx] = &ast_null_frame;
03641 }
03642 }
03643 }
03644 }
03645 if (conf->transframe[idx]) {
03646 if ((conf->transframe[idx]->frametype != AST_FRAME_NULL) &&
03647 can_write(chan, confflags)) {
03648 struct ast_frame *cur;
03649
03650
03651
03652 for (cur = conf->transframe[idx]; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
03653 if (ast_write(chan, cur)) {
03654 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
03655 break;
03656 }
03657 }
03658 if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
03659 mohtempstopped = 0;
03660 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03661 }
03662 }
03663 } else {
03664 ast_mutex_unlock(&conf->listenlock);
03665 goto bailoutandtrynormal;
03666 }
03667 ast_mutex_unlock(&conf->listenlock);
03668 } else {
03669 bailoutandtrynormal:
03670 if (musiconhold && !ast_dsp_silence(dsp, &fr, &confsilence) && confsilence < MEETME_DELAYDETECTTALK) {
03671 ast_moh_stop(chan);
03672 mohtempstopped = 1;
03673 }
03674 if (user->listen.actual) {
03675 ast_frame_adjust_volume(&fr, user->listen.actual);
03676 }
03677 if (can_write(chan, confflags) && ast_write(chan, &fr) < 0) {
03678 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
03679 }
03680 if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
03681 mohtempstopped = 0;
03682 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03683 }
03684 }
03685 } else {
03686 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
03687 }
03688 }
03689 lastmarked = currentmarked;
03690 }
03691 }
03692
03693 if (musiconhold) {
03694 ast_moh_stop(chan);
03695 }
03696
03697 if (using_pseudo) {
03698 close(fd);
03699 } else {
03700
03701 dahdic.chan = 0;
03702 dahdic.confno = 0;
03703 dahdic.confmode = 0;
03704 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03705 ast_log(LOG_WARNING, "Error setting conference\n");
03706 }
03707 }
03708
03709 reset_volumes(user);
03710
03711 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
03712 !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
03713 conf_play(chan, conf, LEAVE);
03714 }
03715
03716 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) || ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
03717 struct announce_listitem *item;
03718 if (!(item = ao2_alloc(sizeof(*item), NULL)))
03719 goto outrun;
03720 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
03721 ast_copy_string(item->language, chan->language, sizeof(item->language));
03722 item->confchan = conf->chan;
03723 item->confusers = conf->users;
03724 item->announcetype = CONF_HASLEFT;
03725 ast_mutex_lock(&conf->announcelistlock);
03726 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
03727 ast_cond_signal(&conf->announcelist_addition);
03728 ast_mutex_unlock(&conf->announcelistlock);
03729 } else if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
03730 ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW)) && conf->users == 1) {
03731
03732 ast_filedelete(user->namerecloc, NULL);
03733 }
03734
03735 outrun:
03736 AST_LIST_LOCK(&confs);
03737
03738 if (dsp) {
03739 ast_dsp_free(dsp);
03740 }
03741
03742 if (user->user_no) {
03743
03744 now = ast_tvnow();
03745
03746 if (sent_event) {
03747 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeLeave",
03748 "Channel: %s\r\n"
03749 "Uniqueid: %s\r\n"
03750 "Meetme: %s\r\n"
03751 "Usernum: %d\r\n"
03752 "CallerIDNum: %s\r\n"
03753 "CallerIDName: %s\r\n"
03754 "ConnectedLineNum: %s\r\n"
03755 "ConnectedLineName: %s\r\n"
03756 "Duration: %ld\r\n",
03757 chan->name, chan->uniqueid, conf->confno,
03758 user->user_no,
03759 S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
03760 S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<unknown>"),
03761 S_COR(user->chan->connected.id.number.valid, user->chan->connected.id.number.str, "<unknown>"),
03762 S_COR(user->chan->connected.id.name.valid, user->chan->connected.id.name.str, "<unknown>"),
03763 (long)(now.tv_sec - user->jointime));
03764 }
03765
03766 if (setusercount) {
03767 conf->users--;
03768 if (rt_log_members) {
03769
03770 snprintf(members, sizeof(members), "%d", conf->users);
03771 ast_realtime_require_field("meetme",
03772 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
03773 "members", RQ_UINTEGER1, strlen(members),
03774 NULL);
03775 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
03776 }
03777 if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
03778 conf->markedusers--;
03779 }
03780 }
03781
03782 ao2_unlink(conf->usercontainer, user);
03783
03784
03785 if (!conf->users) {
03786 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
03787 }
03788
03789
03790 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
03791 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
03792
03793
03794 if (rt_schedule) {
03795 pbx_builtin_setvar_helper(chan, "MEETMEBOOKID", conf->bookid);
03796 }
03797 }
03798 ao2_ref(user, -1);
03799 AST_LIST_UNLOCK(&confs);
03800
03801 return ret;
03802 }
03803
03804 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
03805 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags64 *confflags, int *too_early, char **optargs)
03806 {
03807 struct ast_variable *var, *origvar;
03808 struct ast_conference *cnf;
03809
03810 *too_early = 0;
03811
03812
03813 AST_LIST_LOCK(&confs);
03814 AST_LIST_TRAVERSE(&confs, cnf, list) {
03815 if (!strcmp(confno, cnf->confno)) {
03816 break;
03817 }
03818 }
03819 if (cnf) {
03820 cnf->refcount += refcount;
03821 }
03822 AST_LIST_UNLOCK(&confs);
03823
03824 if (!cnf) {
03825 char *pin = NULL, *pinadmin = NULL;
03826 int maxusers = 0;
03827 struct timeval now;
03828 char recordingfilename[256] = "";
03829 char recordingformat[11] = "";
03830 char currenttime[32] = "";
03831 char eatime[32] = "";
03832 char bookid[51] = "";
03833 char recordingtmp[AST_MAX_EXTENSION] = "";
03834 char useropts[OPTIONS_LEN + 1] = "";
03835 char adminopts[OPTIONS_LEN + 1] = "";
03836 struct ast_tm tm, etm;
03837 struct timeval endtime = { .tv_sec = 0 };
03838 const char *var2;
03839
03840 if (rt_schedule) {
03841 now = ast_tvnow();
03842
03843 ast_localtime(&now, &tm, NULL);
03844 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
03845
03846 ast_debug(1, "Looking for conference %s that starts after %s\n", confno, currenttime);
03847
03848 var = ast_load_realtime("meetme", "confno",
03849 confno, "starttime <= ", currenttime, "endtime >= ",
03850 currenttime, NULL);
03851
03852 if (!var && fuzzystart) {
03853 now = ast_tvnow();
03854 now.tv_sec += fuzzystart;
03855
03856 ast_localtime(&now, &tm, NULL);
03857 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
03858 var = ast_load_realtime("meetme", "confno",
03859 confno, "starttime <= ", currenttime, "endtime >= ",
03860 currenttime, NULL);
03861 }
03862
03863 if (!var && earlyalert) {
03864 now = ast_tvnow();
03865 now.tv_sec += earlyalert;
03866 ast_localtime(&now, &etm, NULL);
03867 ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
03868 var = ast_load_realtime("meetme", "confno",
03869 confno, "starttime <= ", eatime, "endtime >= ",
03870 currenttime, NULL);
03871 if (var) {
03872 *too_early = 1;
03873 }
03874 }
03875
03876 } else {
03877 var = ast_load_realtime("meetme", "confno", confno, NULL);
03878 }
03879
03880 if (!var) {
03881 return NULL;
03882 }
03883
03884 if (rt_schedule && *too_early) {
03885
03886 if (!ast_streamfile(chan, "conf-has-not-started", chan->language)) {
03887 ast_waitstream(chan, "");
03888 }
03889 ast_variables_destroy(var);
03890 return NULL;
03891 }
03892
03893 for (origvar = var; var; var = var->next) {
03894 if (!strcasecmp(var->name, "pin")) {
03895 pin = ast_strdupa(var->value);
03896 } else if (!strcasecmp(var->name, "adminpin")) {
03897 pinadmin = ast_strdupa(var->value);
03898 } else if (!strcasecmp(var->name, "bookId")) {
03899 ast_copy_string(bookid, var->value, sizeof(bookid));
03900 } else if (!strcasecmp(var->name, "opts")) {
03901 ast_copy_string(useropts, var->value, sizeof(char[OPTIONS_LEN + 1]));
03902 } else if (!strcasecmp(var->name, "maxusers")) {
03903 maxusers = atoi(var->value);
03904 } else if (!strcasecmp(var->name, "adminopts")) {
03905 ast_copy_string(adminopts, var->value, sizeof(char[OPTIONS_LEN + 1]));
03906 } else if (!strcasecmp(var->name, "recordingfilename")) {
03907 ast_copy_string(recordingfilename, var->value, sizeof(recordingfilename));
03908 } else if (!strcasecmp(var->name, "recordingformat")) {
03909 ast_copy_string(recordingformat, var->value, sizeof(recordingformat));
03910 } else if (!strcasecmp(var->name, "endtime")) {
03911 struct ast_tm endtime_tm;
03912 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
03913 endtime = ast_mktime(&endtime_tm, NULL);
03914 }
03915 }
03916
03917 ast_variables_destroy(origvar);
03918
03919 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan, NULL);
03920
03921 if (cnf) {
03922 struct ast_flags64 tmp_flags;
03923
03924 cnf->maxusers = maxusers;
03925 cnf->endalert = endalert;
03926 cnf->endtime = endtime.tv_sec;
03927 cnf->useropts = ast_strdup(useropts);
03928 cnf->adminopts = ast_strdup(adminopts);
03929 cnf->bookid = ast_strdup(bookid);
03930 if (!ast_strlen_zero(recordingfilename)) {
03931 cnf->recordingfilename = ast_strdup(recordingfilename);
03932 }
03933 if (!ast_strlen_zero(recordingformat)) {
03934 cnf->recordingformat = ast_strdup(recordingformat);
03935 }
03936
03937
03938
03939 ast_app_parse_options64(meetme_opts, &tmp_flags, optargs, useropts);
03940 ast_copy_flags64(confflags, &tmp_flags, tmp_flags.flags);
03941
03942 if (strchr(cnf->useropts, 'r')) {
03943 if (ast_strlen_zero(recordingfilename)) {
03944 ast_channel_lock(chan);
03945 if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
03946 ast_free(cnf->recordingfilename);
03947 cnf->recordingfilename = ast_strdup(var2);
03948 }
03949 ast_channel_unlock(chan);
03950 if (ast_strlen_zero(cnf->recordingfilename)) {
03951 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", cnf->confno, chan->uniqueid);
03952 ast_free(cnf->recordingfilename);
03953 cnf->recordingfilename = ast_strdup(recordingtmp);
03954 }
03955 }
03956 if (ast_strlen_zero(cnf->recordingformat)) {
03957 ast_channel_lock(chan);
03958 if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
03959 ast_free(cnf->recordingformat);
03960 cnf->recordingformat = ast_strdup(var2);
03961 }
03962 ast_channel_unlock(chan);
03963 if (ast_strlen_zero(cnf->recordingformat)) {
03964 ast_free(cnf->recordingformat);
03965 cnf->recordingformat = ast_strdup("wav");
03966 }
03967 }
03968 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
03969 }
03970 }
03971 }
03972
03973 if (cnf) {
03974 if (confflags->flags && !cnf->chan &&
03975 !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03976 ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW)) {
03977 ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
03978 ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW);
03979 }
03980
03981 if (confflags && !cnf->chan &&
03982 ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
03983 ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
03984 ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
03985 }
03986 }
03987
03988 return cnf;
03989 }
03990
03991
03992 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
03993 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags64 *confflags)
03994 {
03995 struct ast_config *cfg;
03996 struct ast_variable *var;
03997 struct ast_flags config_flags = { 0 };
03998 struct ast_conference *cnf;
03999
04000 AST_DECLARE_APP_ARGS(args,
04001 AST_APP_ARG(confno);
04002 AST_APP_ARG(pin);
04003 AST_APP_ARG(pinadmin);
04004 );
04005
04006
04007 ast_debug(1, "The requested confno is '%s'?\n", confno);
04008 AST_LIST_LOCK(&confs);
04009 AST_LIST_TRAVERSE(&confs, cnf, list) {
04010 ast_debug(3, "Does conf %s match %s?\n", confno, cnf->confno);
04011 if (!strcmp(confno, cnf->confno))
04012 break;
04013 }
04014 if (cnf) {
04015 cnf->refcount += refcount;
04016 }
04017 AST_LIST_UNLOCK(&confs);
04018
04019 if (!cnf) {
04020 if (dynamic) {
04021
04022 ast_debug(1, "Building dynamic conference '%s'\n", confno);
04023 if (dynamic_pin) {
04024 if (dynamic_pin[0] == 'q') {
04025
04026 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
04027 return NULL;
04028 }
04029 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan, NULL);
04030 } else {
04031 cnf = build_conf(confno, "", "", make, dynamic, refcount, chan, NULL);
04032 }
04033 } else {
04034
04035 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
04036 if (!cfg) {
04037 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
04038 return NULL;
04039 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
04040 ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format. Aborting.\n");
04041 return NULL;
04042 }
04043
04044 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
04045 char parse[MAX_SETTINGS];
04046
04047 if (strcasecmp(var->name, "conf"))
04048 continue;
04049
04050 ast_copy_string(parse, var->value, sizeof(parse));
04051
04052 AST_STANDARD_APP_ARGS(args, parse);
04053 ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
04054 if (!strcasecmp(args.confno, confno)) {
04055
04056 cnf = build_conf(args.confno,
04057 S_OR(args.pin, ""),
04058 S_OR(args.pinadmin, ""),
04059 make, dynamic, refcount, chan, NULL);
04060 break;
04061 }
04062 }
04063 if (!var) {
04064 ast_debug(1, "%s isn't a valid conference\n", confno);
04065 }
04066 ast_config_destroy(cfg);
04067 }
04068 } else if (dynamic_pin) {
04069
04070
04071
04072 if (dynamic_pin[0] == 'q') {
04073 dynamic_pin[0] = '\0';
04074 }
04075 }
04076
04077 if (cnf) {
04078 if (confflags && !cnf->chan &&
04079 !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
04080 ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW)) {
04081 ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
04082 ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW);
04083 }
04084
04085 if (confflags && !cnf->chan &&
04086 ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
04087 ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
04088 ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
04089 }
04090 }
04091
04092 return cnf;
04093 }
04094
04095
04096 static int count_exec(struct ast_channel *chan, const char *data)
04097 {
04098 int res = 0;
04099 struct ast_conference *conf;
04100 int count;
04101 char *localdata;
04102 char val[80] = "0";
04103 AST_DECLARE_APP_ARGS(args,
04104 AST_APP_ARG(confno);
04105 AST_APP_ARG(varname);
04106 );
04107
04108 if (ast_strlen_zero(data)) {
04109 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
04110 return -1;
04111 }
04112
04113 if (!(localdata = ast_strdupa(data)))
04114 return -1;
04115
04116 AST_STANDARD_APP_ARGS(args, localdata);
04117
04118 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
04119
04120 if (conf) {
04121 count = conf->users;
04122 dispose_conf(conf);
04123 conf = NULL;
04124 } else
04125 count = 0;
04126
04127 if (!ast_strlen_zero(args.varname)) {
04128
04129 snprintf(val, sizeof(val), "%d", count);
04130 pbx_builtin_setvar_helper(chan, args.varname, val);
04131 } else {
04132 if (chan->_state != AST_STATE_UP) {
04133 ast_answer(chan);
04134 }
04135 res = ast_say_number(chan, count, "", chan->language, (char *) NULL);
04136 }
04137
04138 return res;
04139 }
04140
04141
04142 static int conf_exec(struct ast_channel *chan, const char *data)
04143 {
04144 int res = -1;
04145 char confno[MAX_CONFNUM] = "";
04146 int allowretry = 0;
04147 int retrycnt = 0;
04148 struct ast_conference *cnf = NULL;
04149 struct ast_flags64 confflags = {0};
04150 struct ast_flags config_flags = { 0 };
04151 int dynamic = 0;
04152 int empty = 0, empty_no_pin = 0;
04153 int always_prompt = 0;
04154 const char *notdata;
04155 char *info, the_pin[MAX_PIN] = "";
04156 AST_DECLARE_APP_ARGS(args,
04157 AST_APP_ARG(confno);
04158 AST_APP_ARG(options);
04159 AST_APP_ARG(pin);
04160 );
04161 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
04162
04163 if (ast_strlen_zero(data)) {
04164 allowretry = 1;
04165 notdata = "";
04166 } else {
04167 notdata = data;
04168 }
04169
04170 if (chan->_state != AST_STATE_UP)
04171 ast_answer(chan);
04172
04173 info = ast_strdupa(notdata);
04174
04175 AST_STANDARD_APP_ARGS(args, info);
04176
04177 if (args.confno) {
04178 ast_copy_string(confno, args.confno, sizeof(confno));
04179 if (ast_strlen_zero(confno)) {
04180 allowretry = 1;
04181 }
04182 }
04183
04184 if (args.pin)
04185 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
04186
04187 if (args.options) {
04188 ast_app_parse_options64(meetme_opts, &confflags, optargs, args.options);
04189 dynamic = ast_test_flag64(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
04190 if (ast_test_flag64(&confflags, CONFFLAG_DYNAMICPIN) && ast_strlen_zero(args.pin))
04191 strcpy(the_pin, "q");
04192
04193 empty = ast_test_flag64(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
04194 empty_no_pin = ast_test_flag64(&confflags, CONFFLAG_EMPTYNOPIN);
04195 always_prompt = ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT | CONFFLAG_DYNAMICPIN);
04196 }
04197
04198 do {
04199 if (retrycnt > 3)
04200 allowretry = 0;
04201 if (empty) {
04202 int i;
04203 struct ast_config *cfg;
04204 struct ast_variable *var;
04205 int confno_int;
04206
04207
04208 if ((empty_no_pin) || (!dynamic)) {
04209 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
04210 if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
04211 var = ast_variable_browse(cfg, "rooms");
04212 while (var) {
04213 char parse[MAX_SETTINGS], *stringp = parse, *confno_tmp;
04214 if (!strcasecmp(var->name, "conf")) {
04215 int found = 0;
04216 ast_copy_string(parse, var->value, sizeof(parse));
04217 confno_tmp = strsep(&stringp, "|,");
04218 if (!dynamic) {
04219
04220 AST_LIST_LOCK(&confs);
04221 AST_LIST_TRAVERSE(&confs, cnf, list) {
04222 if (!strcmp(confno_tmp, cnf->confno)) {
04223
04224 found = 1;
04225 break;
04226 }
04227 }
04228 AST_LIST_UNLOCK(&confs);
04229 if (!found) {
04230
04231 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
04232
04233
04234
04235
04236 ast_copy_string(confno, confno_tmp, sizeof(confno));
04237 break;
04238 }
04239 }
04240 }
04241 }
04242 var = var->next;
04243 }
04244 ast_config_destroy(cfg);
04245 }
04246
04247 if (ast_strlen_zero(confno) && (cfg = ast_load_realtime_multientry("meetme", "confno LIKE", "%", SENTINEL))) {
04248 const char *catg;
04249 for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
04250 const char *confno_tmp = ast_variable_retrieve(cfg, catg, "confno");
04251 const char *pin_tmp = ast_variable_retrieve(cfg, catg, "pin");
04252 if (ast_strlen_zero(confno_tmp)) {
04253 continue;
04254 }
04255 if (!dynamic) {
04256 int found = 0;
04257
04258 AST_LIST_LOCK(&confs);
04259 AST_LIST_TRAVERSE(&confs, cnf, list) {
04260 if (!strcmp(confno_tmp, cnf->confno)) {
04261
04262 found = 1;
04263 break;
04264 }
04265 }
04266 AST_LIST_UNLOCK(&confs);
04267 if (!found) {
04268
04269 if ((empty_no_pin && ast_strlen_zero(pin_tmp)) || (!empty_no_pin)) {
04270
04271
04272
04273
04274 ast_copy_string(confno, confno_tmp, sizeof(confno));
04275 break;
04276 }
04277 }
04278 }
04279 }
04280 ast_config_destroy(cfg);
04281 }
04282 }
04283
04284
04285 if (ast_strlen_zero(confno) && dynamic) {
04286 AST_LIST_LOCK(&confs);
04287 for (i = 0; i < ARRAY_LEN(conf_map); i++) {
04288 if (!conf_map[i]) {
04289 snprintf(confno, sizeof(confno), "%d", i);
04290 conf_map[i] = 1;
04291 break;
04292 }
04293 }
04294 AST_LIST_UNLOCK(&confs);
04295 }
04296
04297
04298 if (ast_strlen_zero(confno)) {
04299 res = ast_streamfile(chan, "conf-noempty", chan->language);
04300 if (!res)
04301 ast_waitstream(chan, "");
04302 } else {
04303 if (sscanf(confno, "%30d", &confno_int) == 1) {
04304 if (!ast_test_flag64(&confflags, CONFFLAG_QUIET)) {
04305 res = ast_streamfile(chan, "conf-enteringno", chan->language);
04306 if (!res) {
04307 ast_waitstream(chan, "");
04308 res = ast_say_digits(chan, confno_int, "", chan->language);
04309 }
04310 }
04311 } else {
04312 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
04313 }
04314 }
04315 }
04316
04317 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
04318
04319 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
04320 if (res < 0) {
04321
04322 confno[0] = '\0';
04323 allowretry = 0;
04324 break;
04325 }
04326 }
04327 if (!ast_strlen_zero(confno)) {
04328
04329 cnf = find_conf(chan, confno, 1, dynamic, the_pin,
04330 sizeof(the_pin), 1, &confflags);
04331 if (!cnf) {
04332 int too_early = 0;
04333
04334 cnf = find_conf_realtime(chan, confno, 1, dynamic,
04335 the_pin, sizeof(the_pin), 1, &confflags, &too_early, optargs);
04336 if (rt_schedule && too_early)
04337 allowretry = 0;
04338 }
04339
04340 if (!cnf) {
04341 if (allowretry) {
04342 confno[0] = '\0';
04343 res = ast_streamfile(chan, "conf-invalid", chan->language);
04344 if (!res)
04345 ast_waitstream(chan, "");
04346 res = -1;
04347 }
04348 } else {
04349
04350 int req_pin = !ast_strlen_zero(cnf->pin) ||
04351 (!ast_strlen_zero(cnf->pinadmin) &&
04352 ast_test_flag64(&confflags, CONFFLAG_ADMIN));
04353
04354
04355
04356
04357
04358
04359
04360
04361
04362
04363
04364
04365
04366 int not_exempt = !cnf->isdynamic;
04367 not_exempt = not_exempt || (!ast_strlen_zero(args.pin) && ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT));
04368 not_exempt = not_exempt || (ast_strlen_zero(args.pin) && cnf->users);
04369 if (req_pin && not_exempt) {
04370 char pin[MAX_PIN] = "";
04371 int j;
04372
04373
04374 for (j = 0; j < 3; j++) {
04375 if (*the_pin && (always_prompt == 0)) {
04376 ast_copy_string(pin, the_pin, sizeof(pin));
04377 res = 0;
04378 } else {
04379
04380 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
04381 }
04382 if (res >= 0) {
04383 if ((!strcasecmp(pin, cnf->pin) &&
04384 (ast_strlen_zero(cnf->pinadmin) ||
04385 !ast_test_flag64(&confflags, CONFFLAG_ADMIN))) ||
04386 (!ast_strlen_zero(cnf->pinadmin) &&
04387 !strcasecmp(pin, cnf->pinadmin))) {
04388
04389 allowretry = 0;
04390 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) {
04391 if (!ast_strlen_zero(cnf->adminopts)) {
04392 char *opts = ast_strdupa(cnf->adminopts);
04393 ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04394 }
04395 } else {
04396 if (!ast_strlen_zero(cnf->useropts)) {
04397 char *opts = ast_strdupa(cnf->useropts);
04398 ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04399 }
04400 }
04401
04402 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
04403 res = conf_run(chan, cnf, &confflags, optargs);
04404 break;
04405 } else {
04406
04407 if (!ast_streamfile(chan, "conf-invalidpin", chan->language)) {
04408 res = ast_waitstream(chan, AST_DIGIT_ANY);
04409 ast_stopstream(chan);
04410 } else {
04411 ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
04412 break;
04413 }
04414 if (res < 0)
04415 break;
04416 pin[0] = res;
04417 pin[1] = '\0';
04418 res = -1;
04419 if (allowretry)
04420 confno[0] = '\0';
04421 }
04422 } else {
04423
04424 res = -1;
04425 allowretry = 0;
04426
04427 break;
04428 }
04429
04430
04431 if (*the_pin && (always_prompt == 0)) {
04432 break;
04433 }
04434 }
04435 } else {
04436
04437 allowretry = 0;
04438
04439
04440
04441
04442 if (!ast_strlen_zero(cnf->useropts)) {
04443 char *opts = ast_strdupa(cnf->useropts);
04444 ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04445 }
04446
04447
04448 res = conf_run(chan, cnf, &confflags, optargs);
04449 }
04450 dispose_conf(cnf);
04451 cnf = NULL;
04452 }
04453 }
04454 } while (allowretry);
04455
04456 if (cnf)
04457 dispose_conf(cnf);
04458
04459 return res;
04460 }
04461
04462 static struct ast_conf_user *find_user(struct ast_conference *conf, const char *callerident)
04463 {
04464 struct ast_conf_user *user = NULL;
04465 int cid;
04466
04467 if (conf && callerident && sscanf(callerident, "%30d", &cid) == 1) {
04468 user = ao2_find(conf->usercontainer, &cid, 0);
04469
04470 return user;
04471 }
04472 return NULL;
04473 }
04474
04475 static int user_listen_volup_cb(void *obj, void *unused, int flags)
04476 {
04477 struct ast_conf_user *user = obj;
04478 tweak_listen_volume(user, VOL_UP);
04479 return 0;
04480 }
04481
04482 static int user_listen_voldown_cb(void *obj, void *unused, int flags)
04483 {
04484 struct ast_conf_user *user = obj;
04485 tweak_listen_volume(user, VOL_DOWN);
04486 return 0;
04487 }
04488
04489 static int user_talk_volup_cb(void *obj, void *unused, int flags)
04490 {
04491 struct ast_conf_user *user = obj;
04492 tweak_talk_volume(user, VOL_UP);
04493 return 0;
04494 }
04495
04496 static int user_talk_voldown_cb(void *obj, void *unused, int flags)
04497 {
04498 struct ast_conf_user *user = obj;
04499 tweak_talk_volume(user, VOL_DOWN);
04500 return 0;
04501 }
04502
04503 static int user_reset_vol_cb(void *obj, void *unused, int flags)
04504 {
04505 struct ast_conf_user *user = obj;
04506 reset_volumes(user);
04507 return 0;
04508 }
04509
04510 static int user_chan_cb(void *obj, void *args, int flags)
04511 {
04512 struct ast_conf_user *user = obj;
04513 const char *channel = args;
04514
04515 if (!strcmp(user->chan->name, channel)) {
04516 return (CMP_MATCH | CMP_STOP);
04517 }
04518
04519 return 0;
04520 }
04521
04522
04523
04524
04525 static int admin_exec(struct ast_channel *chan, const char *data) {
04526 char *params;
04527 struct ast_conference *cnf;
04528 struct ast_conf_user *user = NULL;
04529 AST_DECLARE_APP_ARGS(args,
04530 AST_APP_ARG(confno);
04531 AST_APP_ARG(command);
04532 AST_APP_ARG(user);
04533 );
04534 int res = 0;
04535
04536 if (ast_strlen_zero(data)) {
04537 ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
04538 pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
04539 return -1;
04540 }
04541
04542 params = ast_strdupa(data);
04543 AST_STANDARD_APP_ARGS(args, params);
04544
04545 if (!args.command) {
04546 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
04547 pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
04548 return -1;
04549 }
04550
04551 AST_LIST_LOCK(&confs);
04552 AST_LIST_TRAVERSE(&confs, cnf, list) {
04553 if (!strcmp(cnf->confno, args.confno))
04554 break;
04555 }
04556
04557 if (!cnf) {
04558 ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
04559 AST_LIST_UNLOCK(&confs);
04560 pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOTFOUND");
04561 return 0;
04562 }
04563
04564 ast_atomic_fetchadd_int(&cnf->refcount, 1);
04565
04566 if (args.user) {
04567 user = find_user(cnf, args.user);
04568 if (!user) {
04569 ast_log(LOG_NOTICE, "Specified User not found!\n");
04570 res = -2;
04571 goto usernotfound;
04572 }
04573 }
04574
04575 switch (*args.command) {
04576 case 76:
04577 cnf->locked = 1;
04578 break;
04579 case 108:
04580 cnf->locked = 0;
04581 break;
04582 case 75:
04583 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_kickme_cb, NULL);
04584 break;
04585 case 101:
04586 {
04587 int max_no = 0;
04588
04589
04590 if (user) {
04591 ao2_ref(user, -1);
04592 }
04593
04594 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
04595 user = ao2_find(cnf->usercontainer, &max_no, 0);
04596 if (!ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))
04597 user->adminflags |= ADMINFLAG_KICKME;
04598 else {
04599 res = -1;
04600 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
04601 }
04602 ao2_ref(user, -1);
04603 break;
04604 }
04605 case 77:
04606 user->adminflags |= ADMINFLAG_MUTED;
04607 break;
04608 case 78:
04609 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_muted_cb, &cnf);
04610 break;
04611 case 109:
04612 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
04613 break;
04614 case 110:
04615 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, NULL);
04616 break;
04617 case 107:
04618 user->adminflags |= ADMINFLAG_KICKME;
04619 break;
04620 case 118:
04621 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_voldown_cb, NULL);
04622 break;
04623 case 86:
04624 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_volup_cb, NULL);
04625 break;
04626 case 115:
04627 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_voldown_cb, NULL);
04628 break;
04629 case 83:
04630 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_volup_cb, NULL);
04631 break;
04632 case 82:
04633 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_reset_vol_cb, NULL);
04634 break;
04635 case 114:
04636 reset_volumes(user);
04637 break;
04638 case 85:
04639 tweak_listen_volume(user, VOL_UP);
04640 break;
04641 case 117:
04642 tweak_listen_volume(user, VOL_DOWN);
04643 break;
04644 case 84:
04645 tweak_talk_volume(user, VOL_UP);
04646 break;
04647 case 116:
04648 tweak_talk_volume(user, VOL_DOWN);
04649 break;
04650 case 'E':
04651 if (rt_extend_conf(args.confno)) {
04652 res = -1;
04653 }
04654 break;
04655 }
04656
04657 if (args.user) {
04658
04659 ao2_ref(user, -1);
04660 }
04661 usernotfound:
04662 AST_LIST_UNLOCK(&confs);
04663
04664 dispose_conf(cnf);
04665 pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", res == -2 ? "NOTFOUND" : res ? "FAILED" : "OK");
04666
04667 return 0;
04668 }
04669
04670
04671
04672 static int channel_admin_exec(struct ast_channel *chan, const char *data) {
04673 char *params;
04674 struct ast_conference *conf = NULL;
04675 struct ast_conf_user *user = NULL;
04676 AST_DECLARE_APP_ARGS(args,
04677 AST_APP_ARG(channel);
04678 AST_APP_ARG(command);
04679 );
04680
04681 if (ast_strlen_zero(data)) {
04682 ast_log(LOG_WARNING, "MeetMeChannelAdmin requires two arguments!\n");
04683 return -1;
04684 }
04685
04686 params = ast_strdupa(data);
04687 AST_STANDARD_APP_ARGS(args, params);
04688
04689 if (!args.channel) {
04690 ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a channel name!\n");
04691 return -1;
04692 }
04693
04694 if (!args.command) {
04695 ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a command!\n");
04696 return -1;
04697 }
04698
04699 AST_LIST_LOCK(&confs);
04700 AST_LIST_TRAVERSE(&confs, conf, list) {
04701 if ((user = ao2_callback(conf->usercontainer, 0, user_chan_cb, args.channel))) {
04702 break;
04703 }
04704 }
04705
04706 if (!user) {
04707 ast_log(LOG_NOTICE, "Specified user (%s) not found\n", args.channel);
04708 AST_LIST_UNLOCK(&confs);
04709 return 0;
04710 }
04711
04712
04713 switch (*args.command) {
04714 case 77:
04715 user->adminflags |= ADMINFLAG_MUTED;
04716 break;
04717 case 109:
04718 user->adminflags &= ~ADMINFLAG_MUTED;
04719 break;
04720 case 107:
04721 user->adminflags |= ADMINFLAG_KICKME;
04722 break;
04723 default:
04724 ast_log(LOG_WARNING, "Unknown MeetMeChannelAdmin command '%s'\n", args.command);
04725 break;
04726 }
04727 ao2_ref(user, -1);
04728 AST_LIST_UNLOCK(&confs);
04729
04730 return 0;
04731 }
04732
04733 static int meetmemute(struct mansession *s, const struct message *m, int mute)
04734 {
04735 struct ast_conference *conf;
04736 struct ast_conf_user *user;
04737 const char *confid = astman_get_header(m, "Meetme");
04738 char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
04739 int userno;
04740
04741 if (ast_strlen_zero(confid)) {
04742 astman_send_error(s, m, "Meetme conference not specified");
04743 return 0;
04744 }
04745
04746 if (ast_strlen_zero(userid)) {
04747 astman_send_error(s, m, "Meetme user number not specified");
04748 return 0;
04749 }
04750
04751 userno = strtoul(userid, &userid, 10);
04752
04753 if (*userid) {
04754 astman_send_error(s, m, "Invalid user number");
04755 return 0;
04756 }
04757
04758
04759 AST_LIST_LOCK(&confs);
04760 AST_LIST_TRAVERSE(&confs, conf, list) {
04761 if (!strcmp(confid, conf->confno))
04762 break;
04763 }
04764
04765 if (!conf) {
04766 AST_LIST_UNLOCK(&confs);
04767 astman_send_error(s, m, "Meetme conference does not exist");
04768 return 0;
04769 }
04770
04771 user = ao2_find(conf->usercontainer, &userno, 0);
04772
04773 if (!user) {
04774 AST_LIST_UNLOCK(&confs);
04775 astman_send_error(s, m, "User number not found");
04776 return 0;
04777 }
04778
04779 if (mute)
04780 user->adminflags |= ADMINFLAG_MUTED;
04781 else
04782 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
04783
04784 AST_LIST_UNLOCK(&confs);
04785
04786 ast_log(LOG_NOTICE, "Requested to %smute conf %s user %d userchan %s uniqueid %s\n", mute ? "" : "un", conf->confno, user->user_no, user->chan->name, user->chan->uniqueid);
04787
04788 ao2_ref(user, -1);
04789 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
04790 return 0;
04791 }
04792
04793 static int action_meetmemute(struct mansession *s, const struct message *m)
04794 {
04795 return meetmemute(s, m, 1);
04796 }
04797
04798 static int action_meetmeunmute(struct mansession *s, const struct message *m)
04799 {
04800 return meetmemute(s, m, 0);
04801 }
04802
04803 static int action_meetmelist(struct mansession *s, const struct message *m)
04804 {
04805 const char *actionid = astman_get_header(m, "ActionID");
04806 const char *conference = astman_get_header(m, "Conference");
04807 char idText[80] = "";
04808 struct ast_conference *cnf;
04809 struct ast_conf_user *user;
04810 struct ao2_iterator user_iter;
04811 int total = 0;
04812
04813 if (!ast_strlen_zero(actionid))
04814 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
04815
04816 if (AST_LIST_EMPTY(&confs)) {
04817 astman_send_error(s, m, "No active conferences.");
04818 return 0;
04819 }
04820
04821 astman_send_listack(s, m, "Meetme user list will follow", "start");
04822
04823
04824 AST_LIST_LOCK(&confs);
04825 AST_LIST_TRAVERSE(&confs, cnf, list) {
04826
04827 if (!ast_strlen_zero(conference) && strcmp(cnf->confno, conference))
04828 continue;
04829
04830
04831 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
04832 while ((user = ao2_iterator_next(&user_iter))) {
04833 total++;
04834 astman_append(s,
04835 "Event: MeetmeList\r\n"
04836 "%s"
04837 "Conference: %s\r\n"
04838 "UserNumber: %d\r\n"
04839 "CallerIDNum: %s\r\n"
04840 "CallerIDName: %s\r\n"
04841 "ConnectedLineNum: %s\r\n"
04842 "ConnectedLineName: %s\r\n"
04843 "Channel: %s\r\n"
04844 "Admin: %s\r\n"
04845 "Role: %s\r\n"
04846 "MarkedUser: %s\r\n"
04847 "Muted: %s\r\n"
04848 "Talking: %s\r\n"
04849 "\r\n",
04850 idText,
04851 cnf->confno,
04852 user->user_no,
04853 S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
04854 S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<no name>"),
04855 S_COR(user->chan->connected.id.number.valid, user->chan->connected.id.number.str, "<unknown>"),
04856 S_COR(user->chan->connected.id.name.valid, user->chan->connected.id.name.str, "<no name>"),
04857 user->chan->name,
04858 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "Yes" : "No",
04859 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "Listen only" : ast_test_flag64(&user->userflags, CONFFLAG_TALKER) ? "Talk only" : "Talk and listen",
04860 ast_test_flag64(&user->userflags, CONFFLAG_MARKEDUSER) ? "Yes" : "No",
04861 user->adminflags & ADMINFLAG_MUTED ? "By admin" : user->adminflags & ADMINFLAG_SELFMUTED ? "By self" : "No",
04862 user->talking > 0 ? "Yes" : user->talking == 0 ? "No" : "Not monitored");
04863 ao2_ref(user, -1);
04864 }
04865 ao2_iterator_destroy(&user_iter);
04866 }
04867 AST_LIST_UNLOCK(&confs);
04868
04869 astman_append(s,
04870 "Event: MeetmeListComplete\r\n"
04871 "EventList: Complete\r\n"
04872 "ListItems: %d\r\n"
04873 "%s"
04874 "\r\n", total, idText);
04875 return 0;
04876 }
04877
04878
04879
04880
04881
04882
04883
04884 static void filename_parse(char *filename, char *buffer)
04885 {
04886 char *slash;
04887 if (ast_strlen_zero(filename)) {
04888 ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
04889 } else if (filename[0] != '/') {
04890 snprintf(buffer, PATH_MAX, "%s/meetme/%s", ast_config_AST_SPOOL_DIR, filename);
04891 } else {
04892 ast_copy_string(buffer, filename, PATH_MAX);
04893 }
04894
04895 slash = buffer;
04896 if ((slash = strrchr(slash, '/'))) {
04897 *slash = '\0';
04898 ast_mkdir(buffer, 0777);
04899 *slash = '/';
04900 }
04901 }
04902
04903 static void *recordthread(void *args)
04904 {
04905 struct ast_conference *cnf = args;
04906 struct ast_frame *f = NULL;
04907 int flags;
04908 struct ast_filestream *s = NULL;
04909 int res = 0;
04910 int x;
04911 const char *oldrecordingfilename = NULL;
04912 char filename_buffer[PATH_MAX];
04913
04914 if (!cnf || !cnf->lchan) {
04915 pthread_exit(0);
04916 }
04917
04918 filename_buffer[0] = '\0';
04919 filename_parse(cnf->recordingfilename, filename_buffer);
04920
04921 ast_stopstream(cnf->lchan);
04922 flags = O_CREAT | O_TRUNC | O_WRONLY;
04923
04924
04925 cnf->recording = MEETME_RECORD_ACTIVE;
04926 while (ast_waitfor(cnf->lchan, -1) > -1) {
04927 if (cnf->recording == MEETME_RECORD_TERMINATE) {
04928 AST_LIST_LOCK(&confs);
04929 AST_LIST_UNLOCK(&confs);
04930 break;
04931 }
04932 if (!s && !(ast_strlen_zero(filename_buffer)) && (filename_buffer != oldrecordingfilename)) {
04933 s = ast_writefile(filename_buffer, cnf->recordingformat, NULL, flags, 0, AST_FILE_MODE);
04934 oldrecordingfilename = filename_buffer;
04935 }
04936
04937 f = ast_read(cnf->lchan);
04938 if (!f) {
04939 res = -1;
04940 break;
04941 }
04942 if (f->frametype == AST_FRAME_VOICE) {
04943 ast_mutex_lock(&cnf->listenlock);
04944 for (x = 0; x < AST_FRAME_BITS; x++) {
04945
04946 if (cnf->transframe[x]) {
04947 ast_frfree(cnf->transframe[x]);
04948 cnf->transframe[x] = NULL;
04949 }
04950 }
04951 if (cnf->origframe)
04952 ast_frfree(cnf->origframe);
04953 cnf->origframe = ast_frdup(f);
04954 ast_mutex_unlock(&cnf->listenlock);
04955 if (s)
04956 res = ast_writestream(s, f);
04957 if (res) {
04958 ast_frfree(f);
04959 break;
04960 }
04961 }
04962 ast_frfree(f);
04963 }
04964 cnf->recording = MEETME_RECORD_OFF;
04965 if (s)
04966 ast_closestream(s);
04967
04968 pthread_exit(0);
04969 }
04970
04971
04972 static enum ast_device_state meetmestate(const char *data)
04973 {
04974 struct ast_conference *conf;
04975
04976
04977 AST_LIST_LOCK(&confs);
04978 AST_LIST_TRAVERSE(&confs, conf, list) {
04979 if (!strcmp(data, conf->confno))
04980 break;
04981 }
04982 AST_LIST_UNLOCK(&confs);
04983 if (!conf)
04984 return AST_DEVICE_INVALID;
04985
04986
04987
04988 if (!conf->users)
04989 return AST_DEVICE_NOT_INUSE;
04990
04991 return AST_DEVICE_INUSE;
04992 }
04993
04994 static void load_config_meetme(void)
04995 {
04996 struct ast_config *cfg;
04997 struct ast_flags config_flags = { 0 };
04998 const char *val;
04999
05000 if (!(cfg = ast_config_load(CONFIG_FILE_NAME, config_flags))) {
05001 return;
05002 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
05003 ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format. Aborting.\n");
05004 return;
05005 }
05006
05007 audio_buffers = DEFAULT_AUDIO_BUFFERS;
05008
05009
05010 rt_schedule = 0;
05011 fuzzystart = 0;
05012 earlyalert = 0;
05013 endalert = 0;
05014 extendby = 0;
05015
05016
05017 rt_log_members = 1;
05018
05019 if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
05020 if ((sscanf(val, "%30d", &audio_buffers) != 1)) {
05021 ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
05022 audio_buffers = DEFAULT_AUDIO_BUFFERS;
05023 } else if ((audio_buffers < DAHDI_DEFAULT_NUM_BUFS) || (audio_buffers > DAHDI_MAX_NUM_BUFS)) {
05024 ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
05025 DAHDI_DEFAULT_NUM_BUFS, DAHDI_MAX_NUM_BUFS);
05026 audio_buffers = DEFAULT_AUDIO_BUFFERS;
05027 }
05028 if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
05029 ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
05030 }
05031
05032 if ((val = ast_variable_retrieve(cfg, "general", "schedule")))
05033 rt_schedule = ast_true(val);
05034 if ((val = ast_variable_retrieve(cfg, "general", "logmembercount")))
05035 rt_log_members = ast_true(val);
05036 if ((val = ast_variable_retrieve(cfg, "general", "fuzzystart"))) {
05037 if ((sscanf(val, "%30d", &fuzzystart) != 1)) {
05038 ast_log(LOG_WARNING, "fuzzystart must be a number, not '%s'\n", val);
05039 fuzzystart = 0;
05040 }
05041 }
05042 if ((val = ast_variable_retrieve(cfg, "general", "earlyalert"))) {
05043 if ((sscanf(val, "%30d", &earlyalert) != 1)) {
05044 ast_log(LOG_WARNING, "earlyalert must be a number, not '%s'\n", val);
05045 earlyalert = 0;
05046 }
05047 }
05048 if ((val = ast_variable_retrieve(cfg, "general", "endalert"))) {
05049 if ((sscanf(val, "%30d", &endalert) != 1)) {
05050 ast_log(LOG_WARNING, "endalert must be a number, not '%s'\n", val);
05051 endalert = 0;
05052 }
05053 }
05054 if ((val = ast_variable_retrieve(cfg, "general", "extendby"))) {
05055 if ((sscanf(val, "%30d", &extendby) != 1)) {
05056 ast_log(LOG_WARNING, "extendby must be a number, not '%s'\n", val);
05057 extendby = 0;
05058 }
05059 }
05060
05061 ast_config_destroy(cfg);
05062 }
05063
05064
05065
05066
05067 static struct sla_trunk *sla_find_trunk(const char *name)
05068 {
05069 struct sla_trunk *trunk = NULL;
05070
05071 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
05072 if (!strcasecmp(trunk->name, name))
05073 break;
05074 }
05075
05076 return trunk;
05077 }
05078
05079
05080
05081
05082 static struct sla_station *sla_find_station(const char *name)
05083 {
05084 struct sla_station *station = NULL;
05085
05086 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
05087 if (!strcasecmp(station->name, name))
05088 break;
05089 }
05090
05091 return station;
05092 }
05093
05094 static int sla_check_station_hold_access(const struct sla_trunk *trunk,
05095 const struct sla_station *station)
05096 {
05097 struct sla_station_ref *station_ref;
05098 struct sla_trunk_ref *trunk_ref;
05099
05100
05101 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
05102 AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
05103 if (trunk_ref->trunk != trunk || station_ref->station == station)
05104 continue;
05105 if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME &&
05106 station_ref->station->hold_access == SLA_HOLD_PRIVATE)
05107 return 1;
05108 return 0;
05109 }
05110 }
05111
05112 return 0;
05113 }
05114
05115
05116
05117
05118
05119
05120
05121
05122 static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station *station,
05123 const char *name)
05124 {
05125 struct sla_trunk_ref *trunk_ref = NULL;
05126
05127 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05128 if (strcasecmp(trunk_ref->trunk->name, name))
05129 continue;
05130
05131 if ( (trunk_ref->trunk->barge_disabled
05132 && trunk_ref->state == SLA_TRUNK_STATE_UP) ||
05133 (trunk_ref->trunk->hold_stations
05134 && trunk_ref->trunk->hold_access == SLA_HOLD_PRIVATE
05135 && trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) ||
05136 sla_check_station_hold_access(trunk_ref->trunk, station) )
05137 {
05138 trunk_ref = NULL;
05139 }
05140
05141 break;
05142 }
05143
05144 return trunk_ref;
05145 }
05146
05147 static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
05148 {
05149 struct sla_station_ref *station_ref;
05150
05151 if (!(station_ref = ast_calloc(1, sizeof(*station_ref))))
05152 return NULL;
05153
05154 station_ref->station = station;
05155
05156 return station_ref;
05157 }
05158
05159 static struct sla_ringing_station *sla_create_ringing_station(struct sla_station *station)
05160 {
05161 struct sla_ringing_station *ringing_station;
05162
05163 if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
05164 return NULL;
05165
05166 ringing_station->station = station;
05167 ringing_station->ring_begin = ast_tvnow();
05168
05169 return ringing_station;
05170 }
05171
05172 static enum ast_device_state sla_state_to_devstate(enum sla_trunk_state state)
05173 {
05174 switch (state) {
05175 case SLA_TRUNK_STATE_IDLE:
05176 return AST_DEVICE_NOT_INUSE;
05177 case SLA_TRUNK_STATE_RINGING:
05178 return AST_DEVICE_RINGING;
05179 case SLA_TRUNK_STATE_UP:
05180 return AST_DEVICE_INUSE;
05181 case SLA_TRUNK_STATE_ONHOLD:
05182 case SLA_TRUNK_STATE_ONHOLD_BYME:
05183 return AST_DEVICE_ONHOLD;
05184 }
05185
05186 return AST_DEVICE_UNKNOWN;
05187 }
05188
05189 static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state,
05190 enum sla_which_trunk_refs inactive_only, const struct sla_trunk_ref *exclude)
05191 {
05192 struct sla_station *station;
05193 struct sla_trunk_ref *trunk_ref;
05194
05195 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
05196 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05197 if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
05198 || trunk_ref == exclude)
05199 continue;
05200 trunk_ref->state = state;
05201 ast_devstate_changed(sla_state_to_devstate(state),
05202 "SLA:%s_%s", station->name, trunk->name);
05203 break;
05204 }
05205 }
05206 }
05207
05208 struct run_station_args {
05209 struct sla_station *station;
05210 struct sla_trunk_ref *trunk_ref;
05211 ast_mutex_t *cond_lock;
05212 ast_cond_t *cond;
05213 };
05214
05215 static void answer_trunk_chan(struct ast_channel *chan)
05216 {
05217 ast_answer(chan);
05218 ast_indicate(chan, -1);
05219 }
05220
05221 static void *run_station(void *data)
05222 {
05223 struct sla_station *station;
05224 struct sla_trunk_ref *trunk_ref;
05225 struct ast_str *conf_name = ast_str_create(16);
05226 struct ast_flags64 conf_flags = { 0 };
05227 struct ast_conference *conf;
05228
05229 {
05230 struct run_station_args *args = data;
05231 station = args->station;
05232 trunk_ref = args->trunk_ref;
05233 ast_mutex_lock(args->cond_lock);
05234 ast_cond_signal(args->cond);
05235 ast_mutex_unlock(args->cond_lock);
05236
05237 }
05238
05239 ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
05240 ast_str_set(&conf_name, 0, "SLA_%s", trunk_ref->trunk->name);
05241 ast_set_flag64(&conf_flags,
05242 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
05243 answer_trunk_chan(trunk_ref->chan);
05244 conf = build_conf(ast_str_buffer(conf_name), "", "", 0, 0, 1, trunk_ref->chan, NULL);
05245 if (conf) {
05246 conf_run(trunk_ref->chan, conf, &conf_flags, NULL);
05247 dispose_conf(conf);
05248 conf = NULL;
05249 }
05250 trunk_ref->chan = NULL;
05251 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
05252 trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
05253 ast_str_append(&conf_name, 0, ",K");
05254 admin_exec(NULL, ast_str_buffer(conf_name));
05255 trunk_ref->trunk->hold_stations = 0;
05256 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
05257 }
05258
05259 ast_dial_join(station->dial);
05260 ast_dial_destroy(station->dial);
05261 station->dial = NULL;
05262 ast_free(conf_name);
05263
05264 return NULL;
05265 }
05266
05267 static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
05268 {
05269 char buf[80];
05270 struct sla_station_ref *station_ref;
05271
05272 snprintf(buf, sizeof(buf), "SLA_%s,K", ringing_trunk->trunk->name);
05273 admin_exec(NULL, buf);
05274 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
05275
05276 while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry)))
05277 ast_free(station_ref);
05278
05279 ast_free(ringing_trunk);
05280 }
05281
05282 static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
05283 enum sla_station_hangup hangup)
05284 {
05285 struct sla_ringing_trunk *ringing_trunk;
05286 struct sla_trunk_ref *trunk_ref;
05287 struct sla_station_ref *station_ref;
05288
05289 ast_dial_join(ringing_station->station->dial);
05290 ast_dial_destroy(ringing_station->station->dial);
05291 ringing_station->station->dial = NULL;
05292
05293 if (hangup == SLA_STATION_HANGUP_NORMAL)
05294 goto done;
05295
05296
05297
05298
05299
05300
05301 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
05302 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
05303 if (ringing_trunk->trunk == trunk_ref->trunk)
05304 break;
05305 }
05306 if (!trunk_ref)
05307 continue;
05308 if (!(station_ref = sla_create_station_ref(ringing_station->station)))
05309 continue;
05310 AST_LIST_INSERT_TAIL(&ringing_trunk->timed_out_stations, station_ref, entry);
05311 }
05312
05313 done:
05314 ast_free(ringing_station);
05315 }
05316
05317 static void sla_dial_state_callback(struct ast_dial *dial)
05318 {
05319 sla_queue_event(SLA_EVENT_DIAL_STATE);
05320 }
05321
05322
05323
05324
05325 static int sla_check_timed_out_station(const struct sla_ringing_trunk *ringing_trunk,
05326 const struct sla_station *station)
05327 {
05328 struct sla_station_ref *timed_out_station;
05329
05330 AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
05331 if (station == timed_out_station->station)
05332 return 1;
05333 }
05334
05335 return 0;
05336 }
05337
05338
05339
05340
05341
05342
05343
05344
05345
05346 static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *station,
05347 struct sla_trunk_ref **trunk_ref, int rm)
05348 {
05349 struct sla_trunk_ref *s_trunk_ref;
05350 struct sla_ringing_trunk *ringing_trunk = NULL;
05351
05352 AST_LIST_TRAVERSE(&station->trunks, s_trunk_ref, entry) {
05353 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
05354
05355 if (s_trunk_ref->trunk != ringing_trunk->trunk)
05356 continue;
05357
05358
05359
05360 if (sla_check_timed_out_station(ringing_trunk, station))
05361 continue;
05362
05363 if (rm)
05364 AST_LIST_REMOVE_CURRENT(entry);
05365
05366 if (trunk_ref)
05367 *trunk_ref = s_trunk_ref;
05368
05369 break;
05370 }
05371 AST_LIST_TRAVERSE_SAFE_END;
05372
05373 if (ringing_trunk)
05374 break;
05375 }
05376
05377 return ringing_trunk;
05378 }
05379
05380 static void sla_handle_dial_state_event(void)
05381 {
05382 struct sla_ringing_station *ringing_station;
05383
05384 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
05385 struct sla_trunk_ref *s_trunk_ref = NULL;
05386 struct sla_ringing_trunk *ringing_trunk = NULL;
05387 struct run_station_args args;
05388 enum ast_dial_result dial_res;
05389 pthread_t dont_care;
05390 ast_mutex_t cond_lock;
05391 ast_cond_t cond;
05392
05393 switch ((dial_res = ast_dial_state(ringing_station->station->dial))) {
05394 case AST_DIAL_RESULT_HANGUP:
05395 case AST_DIAL_RESULT_INVALID:
05396 case AST_DIAL_RESULT_FAILED:
05397 case AST_DIAL_RESULT_TIMEOUT:
05398 case AST_DIAL_RESULT_UNANSWERED:
05399 AST_LIST_REMOVE_CURRENT(entry);
05400 sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
05401 break;
05402 case AST_DIAL_RESULT_ANSWERED:
05403 AST_LIST_REMOVE_CURRENT(entry);
05404
05405 ast_mutex_lock(&sla.lock);
05406 ringing_trunk = sla_choose_ringing_trunk(ringing_station->station, &s_trunk_ref, 1);
05407 ast_mutex_unlock(&sla.lock);
05408 if (!ringing_trunk) {
05409 ast_debug(1, "Found no ringing trunk for station '%s' to answer!\n", ringing_station->station->name);
05410 break;
05411 }
05412
05413 s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
05414
05415 answer_trunk_chan(ringing_trunk->trunk->chan);
05416 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
05417
05418
05419
05420 args.trunk_ref = s_trunk_ref;
05421 args.station = ringing_station->station;
05422 args.cond = &cond;
05423 args.cond_lock = &cond_lock;
05424 ast_free(ringing_trunk);
05425 ast_free(ringing_station);
05426 ast_mutex_init(&cond_lock);
05427 ast_cond_init(&cond, NULL);
05428 ast_mutex_lock(&cond_lock);
05429 ast_pthread_create_detached_background(&dont_care, NULL, run_station, &args);
05430 ast_cond_wait(&cond, &cond_lock);
05431 ast_mutex_unlock(&cond_lock);
05432 ast_mutex_destroy(&cond_lock);
05433 ast_cond_destroy(&cond);
05434 break;
05435 case AST_DIAL_RESULT_TRYING:
05436 case AST_DIAL_RESULT_RINGING:
05437 case AST_DIAL_RESULT_PROGRESS:
05438 case AST_DIAL_RESULT_PROCEEDING:
05439 break;
05440 }
05441 if (dial_res == AST_DIAL_RESULT_ANSWERED) {
05442
05443 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
05444 sla_queue_event(SLA_EVENT_DIAL_STATE);
05445 break;
05446 }
05447 }
05448 AST_LIST_TRAVERSE_SAFE_END;
05449 }
05450
05451
05452
05453
05454 static int sla_check_ringing_station(const struct sla_station *station)
05455 {
05456 struct sla_ringing_station *ringing_station;
05457
05458 AST_LIST_TRAVERSE(&sla.ringing_stations, ringing_station, entry) {
05459 if (station == ringing_station->station)
05460 return 1;
05461 }
05462
05463 return 0;
05464 }
05465
05466
05467
05468
05469 static int sla_check_failed_station(const struct sla_station *station)
05470 {
05471 struct sla_failed_station *failed_station;
05472 int res = 0;
05473
05474 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.failed_stations, failed_station, entry) {
05475 if (station != failed_station->station)
05476 continue;
05477 if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
05478 AST_LIST_REMOVE_CURRENT(entry);
05479 ast_free(failed_station);
05480 break;
05481 }
05482 res = 1;
05483 }
05484 AST_LIST_TRAVERSE_SAFE_END
05485
05486 return res;
05487 }
05488
05489
05490
05491
05492 static int sla_ring_station(struct sla_ringing_trunk *ringing_trunk, struct sla_station *station)
05493 {
05494 char *tech, *tech_data;
05495 struct ast_dial *dial;
05496 struct sla_ringing_station *ringing_station;
05497 enum ast_dial_result res;
05498 int caller_is_saved;
05499 struct ast_party_caller caller;
05500
05501 if (!(dial = ast_dial_create()))
05502 return -1;
05503
05504 ast_dial_set_state_callback(dial, sla_dial_state_callback);
05505 tech_data = ast_strdupa(station->device);
05506 tech = strsep(&tech_data, "/");
05507
05508 if (ast_dial_append(dial, tech, tech_data) == -1) {
05509 ast_dial_destroy(dial);
05510 return -1;
05511 }
05512
05513
05514 caller_is_saved = 0;
05515 if (!sla.attempt_callerid) {
05516 caller_is_saved = 1;
05517 caller = ringing_trunk->trunk->chan->caller;
05518 ast_party_caller_init(&ringing_trunk->trunk->chan->caller);
05519 }
05520
05521 res = ast_dial_run(dial, ringing_trunk->trunk->chan, 1);
05522
05523
05524 if (caller_is_saved) {
05525 ast_party_caller_free(&ringing_trunk->trunk->chan->caller);
05526 ringing_trunk->trunk->chan->caller = caller;
05527 }
05528
05529 if (res != AST_DIAL_RESULT_TRYING) {
05530 struct sla_failed_station *failed_station;
05531 ast_dial_destroy(dial);
05532 if (!(failed_station = ast_calloc(1, sizeof(*failed_station))))
05533 return -1;
05534 failed_station->station = station;
05535 failed_station->last_try = ast_tvnow();
05536 AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
05537 return -1;
05538 }
05539 if (!(ringing_station = sla_create_ringing_station(station))) {
05540 ast_dial_join(dial);
05541 ast_dial_destroy(dial);
05542 return -1;
05543 }
05544
05545 station->dial = dial;
05546
05547 AST_LIST_INSERT_HEAD(&sla.ringing_stations, ringing_station, entry);
05548
05549 return 0;
05550 }
05551
05552
05553
05554 static int sla_check_inuse_station(const struct sla_station *station)
05555 {
05556 struct sla_trunk_ref *trunk_ref;
05557
05558 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05559 if (trunk_ref->chan)
05560 return 1;
05561 }
05562
05563 return 0;
05564 }
05565
05566 static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *station,
05567 const struct sla_trunk *trunk)
05568 {
05569 struct sla_trunk_ref *trunk_ref = NULL;
05570
05571 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05572 if (trunk_ref->trunk == trunk)
05573 break;
05574 }
05575
05576 return trunk_ref;
05577 }
05578
05579
05580
05581
05582
05583
05584 static int sla_check_station_delay(struct sla_station *station,
05585 struct sla_ringing_trunk *ringing_trunk)
05586 {
05587 struct sla_trunk_ref *trunk_ref;
05588 unsigned int delay = UINT_MAX;
05589 int time_left, time_elapsed;
05590
05591 if (!ringing_trunk)
05592 ringing_trunk = sla_choose_ringing_trunk(station, &trunk_ref, 0);
05593 else
05594 trunk_ref = sla_find_trunk_ref(station, ringing_trunk->trunk);
05595
05596 if (!ringing_trunk || !trunk_ref)
05597 return delay;
05598
05599
05600
05601
05602 delay = trunk_ref->ring_delay;
05603 if (!delay)
05604 delay = station->ring_delay;
05605 if (!delay)
05606 return INT_MAX;
05607
05608 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
05609 time_left = (delay * 1000) - time_elapsed;
05610
05611 return time_left;
05612 }
05613
05614
05615
05616
05617 static void sla_ring_stations(void)
05618 {
05619 struct sla_station_ref *station_ref;
05620 struct sla_ringing_trunk *ringing_trunk;
05621
05622
05623
05624 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
05625 AST_LIST_TRAVERSE(&ringing_trunk->trunk->stations, station_ref, entry) {
05626 int time_left;
05627
05628
05629 if (sla_check_ringing_station(station_ref->station))
05630 continue;
05631
05632
05633 if (sla_check_inuse_station(station_ref->station))
05634 continue;
05635
05636
05637
05638 if (sla_check_failed_station(station_ref->station))
05639 continue;
05640
05641
05642
05643 if (sla_check_timed_out_station(ringing_trunk, station_ref->station))
05644 continue;
05645
05646
05647 time_left = sla_check_station_delay(station_ref->station, ringing_trunk);
05648 if (time_left != INT_MAX && time_left > 0)
05649 continue;
05650
05651
05652 sla_ring_station(ringing_trunk, station_ref->station);
05653 }
05654 }
05655
05656 }
05657
05658 static void sla_hangup_stations(void)
05659 {
05660 struct sla_trunk_ref *trunk_ref;
05661 struct sla_ringing_station *ringing_station;
05662
05663 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
05664 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
05665 struct sla_ringing_trunk *ringing_trunk;
05666 ast_mutex_lock(&sla.lock);
05667 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
05668 if (trunk_ref->trunk == ringing_trunk->trunk)
05669 break;
05670 }
05671 ast_mutex_unlock(&sla.lock);
05672 if (ringing_trunk)
05673 break;
05674 }
05675 if (!trunk_ref) {
05676 AST_LIST_REMOVE_CURRENT(entry);
05677 ast_dial_join(ringing_station->station->dial);
05678 ast_dial_destroy(ringing_station->station->dial);
05679 ringing_station->station->dial = NULL;
05680 ast_free(ringing_station);
05681 }
05682 }
05683 AST_LIST_TRAVERSE_SAFE_END
05684 }
05685
05686 static void sla_handle_ringing_trunk_event(void)
05687 {
05688 ast_mutex_lock(&sla.lock);
05689 sla_ring_stations();
05690 ast_mutex_unlock(&sla.lock);
05691
05692
05693 sla_hangup_stations();
05694 }
05695
05696 static void sla_handle_hold_event(struct sla_event *event)
05697 {
05698 ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
05699 event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME;
05700 ast_devstate_changed(AST_DEVICE_ONHOLD, "SLA:%s_%s",
05701 event->station->name, event->trunk_ref->trunk->name);
05702 sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD,
05703 INACTIVE_TRUNK_REFS, event->trunk_ref);
05704
05705 if (event->trunk_ref->trunk->active_stations == 1) {
05706
05707
05708 event->trunk_ref->trunk->on_hold = 1;
05709 ast_indicate(event->trunk_ref->trunk->chan, AST_CONTROL_HOLD);
05710 }
05711
05712 ast_softhangup(event->trunk_ref->chan, AST_SOFTHANGUP_DEV);
05713 event->trunk_ref->chan = NULL;
05714 }
05715
05716
05717
05718
05719
05720 static int sla_calc_trunk_timeouts(unsigned int *timeout)
05721 {
05722 struct sla_ringing_trunk *ringing_trunk;
05723 int res = 0;
05724
05725 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
05726 int time_left, time_elapsed;
05727 if (!ringing_trunk->trunk->ring_timeout)
05728 continue;
05729 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
05730 time_left = (ringing_trunk->trunk->ring_timeout * 1000) - time_elapsed;
05731 if (time_left <= 0) {
05732 pbx_builtin_setvar_helper(ringing_trunk->trunk->chan, "SLATRUNK_STATUS", "RINGTIMEOUT");
05733 AST_LIST_REMOVE_CURRENT(entry);
05734 sla_stop_ringing_trunk(ringing_trunk);
05735 res = 1;
05736 continue;
05737 }
05738 if (time_left < *timeout)
05739 *timeout = time_left;
05740 }
05741 AST_LIST_TRAVERSE_SAFE_END;
05742
05743 return res;
05744 }
05745
05746
05747
05748
05749
05750 static int sla_calc_station_timeouts(unsigned int *timeout)
05751 {
05752 struct sla_ringing_trunk *ringing_trunk;
05753 struct sla_ringing_station *ringing_station;
05754 int res = 0;
05755
05756 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
05757 unsigned int ring_timeout = 0;
05758 int time_elapsed, time_left = INT_MAX, final_trunk_time_left = INT_MIN;
05759 struct sla_trunk_ref *trunk_ref;
05760
05761
05762
05763
05764 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
05765 struct sla_station_ref *station_ref;
05766 int trunk_time_elapsed, trunk_time_left;
05767
05768 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
05769 if (ringing_trunk->trunk == trunk_ref->trunk)
05770 break;
05771 }
05772 if (!ringing_trunk)
05773 continue;
05774
05775
05776
05777 if (!trunk_ref->ring_timeout)
05778 break;
05779
05780
05781
05782
05783 AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
05784 if (station_ref->station == ringing_station->station)
05785 break;
05786 }
05787 if (station_ref)
05788 continue;
05789
05790 trunk_time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
05791 trunk_time_left = (trunk_ref->ring_timeout * 1000) - trunk_time_elapsed;
05792 if (trunk_time_left > final_trunk_time_left)
05793 final_trunk_time_left = trunk_time_left;
05794 }
05795
05796
05797 if (final_trunk_time_left == INT_MIN && !ringing_station->station->ring_timeout)
05798 continue;
05799
05800
05801 if (ringing_station->station->ring_timeout) {
05802 ring_timeout = ringing_station->station->ring_timeout;
05803 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_station->ring_begin);
05804 time_left = (ring_timeout * 1000) - time_elapsed;
05805 }
05806
05807
05808
05809 if (final_trunk_time_left > INT_MIN && final_trunk_time_left < time_left)
05810 time_left = final_trunk_time_left;
05811
05812
05813 if (time_left <= 0) {
05814 AST_LIST_REMOVE_CURRENT(entry);
05815 sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_TIMEOUT);
05816 res = 1;
05817 continue;
05818 }
05819
05820
05821
05822 if (time_left < *timeout)
05823 *timeout = time_left;
05824 }
05825 AST_LIST_TRAVERSE_SAFE_END;
05826
05827 return res;
05828 }
05829
05830
05831
05832
05833 static int sla_calc_station_delays(unsigned int *timeout)
05834 {
05835 struct sla_station *station;
05836 int res = 0;
05837
05838 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
05839 struct sla_ringing_trunk *ringing_trunk;
05840 int time_left;
05841
05842
05843 if (sla_check_ringing_station(station))
05844 continue;
05845
05846
05847 if (sla_check_inuse_station(station))
05848 continue;
05849
05850
05851 if (!(ringing_trunk = sla_choose_ringing_trunk(station, NULL, 0)))
05852 continue;
05853
05854 if ((time_left = sla_check_station_delay(station, ringing_trunk)) == INT_MAX)
05855 continue;
05856
05857
05858
05859
05860 if (time_left <= 0) {
05861 res = 1;
05862 continue;
05863 }
05864
05865 if (time_left < *timeout)
05866 *timeout = time_left;
05867 }
05868
05869 return res;
05870 }
05871
05872
05873
05874 static int sla_process_timers(struct timespec *ts)
05875 {
05876 unsigned int timeout = UINT_MAX;
05877 struct timeval wait;
05878 unsigned int change_made = 0;
05879
05880
05881 if (sla_calc_trunk_timeouts(&timeout))
05882 change_made = 1;
05883
05884
05885 if (sla_calc_station_timeouts(&timeout))
05886 change_made = 1;
05887
05888
05889 if (sla_calc_station_delays(&timeout))
05890 change_made = 1;
05891
05892
05893 if (change_made)
05894 sla_queue_event_nolock(SLA_EVENT_RINGING_TRUNK);
05895
05896
05897 if (timeout == UINT_MAX)
05898 return 0;
05899
05900 if (ts) {
05901 wait = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1000));
05902 ts->tv_sec = wait.tv_sec;
05903 ts->tv_nsec = wait.tv_usec * 1000;
05904 }
05905
05906 return 1;
05907 }
05908
05909 static int sla_load_config(int reload);
05910
05911
05912 static void sla_check_reload(void)
05913 {
05914 struct sla_station *station;
05915 struct sla_trunk *trunk;
05916
05917 ast_mutex_lock(&sla.lock);
05918
05919 if (!AST_LIST_EMPTY(&sla.event_q) || !AST_LIST_EMPTY(&sla.ringing_trunks)
05920 || !AST_LIST_EMPTY(&sla.ringing_stations)) {
05921 ast_mutex_unlock(&sla.lock);
05922 return;
05923 }
05924
05925 AST_RWLIST_RDLOCK(&sla_stations);
05926 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
05927 if (station->ref_count)
05928 break;
05929 }
05930 AST_RWLIST_UNLOCK(&sla_stations);
05931 if (station) {
05932 ast_mutex_unlock(&sla.lock);
05933 return;
05934 }
05935
05936 AST_RWLIST_RDLOCK(&sla_trunks);
05937 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
05938 if (trunk->ref_count)
05939 break;
05940 }
05941 AST_RWLIST_UNLOCK(&sla_trunks);
05942 if (trunk) {
05943 ast_mutex_unlock(&sla.lock);
05944 return;
05945 }
05946
05947
05948 sla_load_config(1);
05949 sla.reload = 0;
05950
05951 ast_mutex_unlock(&sla.lock);
05952 }
05953
05954 static void *sla_thread(void *data)
05955 {
05956 struct sla_failed_station *failed_station;
05957 struct sla_ringing_station *ringing_station;
05958
05959 ast_mutex_lock(&sla.lock);
05960
05961 while (!sla.stop) {
05962 struct sla_event *event;
05963 struct timespec ts = { 0, };
05964 unsigned int have_timeout = 0;
05965
05966 if (AST_LIST_EMPTY(&sla.event_q)) {
05967 if ((have_timeout = sla_process_timers(&ts)))
05968 ast_cond_timedwait(&sla.cond, &sla.lock, &ts);
05969 else
05970 ast_cond_wait(&sla.cond, &sla.lock);
05971 if (sla.stop)
05972 break;
05973 }
05974
05975 if (have_timeout)
05976 sla_process_timers(NULL);
05977
05978 while ((event = AST_LIST_REMOVE_HEAD(&sla.event_q, entry))) {
05979 ast_mutex_unlock(&sla.lock);
05980 switch (event->type) {
05981 case SLA_EVENT_HOLD:
05982 sla_handle_hold_event(event);
05983 break;
05984 case SLA_EVENT_DIAL_STATE:
05985 sla_handle_dial_state_event();
05986 break;
05987 case SLA_EVENT_RINGING_TRUNK:
05988 sla_handle_ringing_trunk_event();
05989 break;
05990 case SLA_EVENT_RELOAD:
05991 sla.reload = 1;
05992 case SLA_EVENT_CHECK_RELOAD:
05993 break;
05994 }
05995 ast_free(event);
05996 ast_mutex_lock(&sla.lock);
05997 }
05998
05999 if (sla.reload) {
06000 sla_check_reload();
06001 }
06002 }
06003
06004 ast_mutex_unlock(&sla.lock);
06005
06006 while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry)))
06007 ast_free(ringing_station);
06008
06009 while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry)))
06010 ast_free(failed_station);
06011
06012 return NULL;
06013 }
06014
06015 struct dial_trunk_args {
06016 struct sla_trunk_ref *trunk_ref;
06017 struct sla_station *station;
06018 ast_mutex_t *cond_lock;
06019 ast_cond_t *cond;
06020 };
06021
06022 static void *dial_trunk(void *data)
06023 {
06024 struct dial_trunk_args *args = data;
06025 struct ast_dial *dial;
06026 char *tech, *tech_data;
06027 enum ast_dial_result dial_res;
06028 char conf_name[MAX_CONFNUM];
06029 struct ast_conference *conf;
06030 struct ast_flags64 conf_flags = { 0 };
06031 struct sla_trunk_ref *trunk_ref = args->trunk_ref;
06032 int caller_is_saved;
06033 struct ast_party_caller caller;
06034
06035 if (!(dial = ast_dial_create())) {
06036 ast_mutex_lock(args->cond_lock);
06037 ast_cond_signal(args->cond);
06038 ast_mutex_unlock(args->cond_lock);
06039 return NULL;
06040 }
06041
06042 tech_data = ast_strdupa(trunk_ref->trunk->device);
06043 tech = strsep(&tech_data, "/");
06044 if (ast_dial_append(dial, tech, tech_data) == -1) {
06045 ast_mutex_lock(args->cond_lock);
06046 ast_cond_signal(args->cond);
06047 ast_mutex_unlock(args->cond_lock);
06048 ast_dial_destroy(dial);
06049 return NULL;
06050 }
06051
06052
06053 caller_is_saved = 0;
06054 if (!sla.attempt_callerid) {
06055 caller_is_saved = 1;
06056 caller = trunk_ref->chan->caller;
06057 ast_party_caller_init(&trunk_ref->chan->caller);
06058 }
06059
06060 dial_res = ast_dial_run(dial, trunk_ref->chan, 1);
06061
06062
06063 if (caller_is_saved) {
06064 ast_party_caller_free(&trunk_ref->chan->caller);
06065 trunk_ref->chan->caller = caller;
06066 }
06067
06068 if (dial_res != AST_DIAL_RESULT_TRYING) {
06069 ast_mutex_lock(args->cond_lock);
06070 ast_cond_signal(args->cond);
06071 ast_mutex_unlock(args->cond_lock);
06072 ast_dial_destroy(dial);
06073 return NULL;
06074 }
06075
06076 for (;;) {
06077 unsigned int done = 0;
06078 switch ((dial_res = ast_dial_state(dial))) {
06079 case AST_DIAL_RESULT_ANSWERED:
06080 trunk_ref->trunk->chan = ast_dial_answered(dial);
06081 case AST_DIAL_RESULT_HANGUP:
06082 case AST_DIAL_RESULT_INVALID:
06083 case AST_DIAL_RESULT_FAILED:
06084 case AST_DIAL_RESULT_TIMEOUT:
06085 case AST_DIAL_RESULT_UNANSWERED:
06086 done = 1;
06087 case AST_DIAL_RESULT_TRYING:
06088 case AST_DIAL_RESULT_RINGING:
06089 case AST_DIAL_RESULT_PROGRESS:
06090 case AST_DIAL_RESULT_PROCEEDING:
06091 break;
06092 }
06093 if (done)
06094 break;
06095 }
06096
06097 if (!trunk_ref->trunk->chan) {
06098 ast_mutex_lock(args->cond_lock);
06099 ast_cond_signal(args->cond);
06100 ast_mutex_unlock(args->cond_lock);
06101 ast_dial_join(dial);
06102 ast_dial_destroy(dial);
06103 return NULL;
06104 }
06105
06106 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
06107 ast_set_flag64(&conf_flags,
06108 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER |
06109 CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
06110 conf = build_conf(conf_name, "", "", 1, 1, 1, trunk_ref->trunk->chan, NULL);
06111
06112 ast_mutex_lock(args->cond_lock);
06113 ast_cond_signal(args->cond);
06114 ast_mutex_unlock(args->cond_lock);
06115
06116 if (conf) {
06117 conf_run(trunk_ref->trunk->chan, conf, &conf_flags, NULL);
06118 dispose_conf(conf);
06119 conf = NULL;
06120 }
06121
06122
06123 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06124
06125 trunk_ref->trunk->chan = NULL;
06126 trunk_ref->trunk->on_hold = 0;
06127
06128 ast_dial_join(dial);
06129 ast_dial_destroy(dial);
06130
06131 return NULL;
06132 }
06133
06134
06135
06136 static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
06137 {
06138 struct sla_trunk_ref *trunk_ref = NULL;
06139
06140 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06141 if (trunk_ref->state == SLA_TRUNK_STATE_IDLE)
06142 break;
06143 }
06144
06145 return trunk_ref;
06146 }
06147
06148 static int sla_station_exec(struct ast_channel *chan, const char *data)
06149 {
06150 char *station_name, *trunk_name;
06151 struct sla_station *station;
06152 struct sla_trunk_ref *trunk_ref = NULL;
06153 char conf_name[MAX_CONFNUM];
06154 struct ast_flags64 conf_flags = { 0 };
06155 struct ast_conference *conf;
06156
06157 if (ast_strlen_zero(data)) {
06158 ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
06159 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06160 return 0;
06161 }
06162
06163 trunk_name = ast_strdupa(data);
06164 station_name = strsep(&trunk_name, "_");
06165
06166 if (ast_strlen_zero(station_name)) {
06167 ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
06168 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06169 return 0;
06170 }
06171
06172 AST_RWLIST_RDLOCK(&sla_stations);
06173 station = sla_find_station(station_name);
06174 if (station)
06175 ast_atomic_fetchadd_int((int *) &station->ref_count, 1);
06176 AST_RWLIST_UNLOCK(&sla_stations);
06177
06178 if (!station) {
06179 ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
06180 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06181 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06182 return 0;
06183 }
06184
06185 AST_RWLIST_RDLOCK(&sla_trunks);
06186 if (!ast_strlen_zero(trunk_name)) {
06187 trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
06188 } else
06189 trunk_ref = sla_choose_idle_trunk(station);
06190 AST_RWLIST_UNLOCK(&sla_trunks);
06191
06192 if (!trunk_ref) {
06193 if (ast_strlen_zero(trunk_name))
06194 ast_log(LOG_NOTICE, "No trunks available for call.\n");
06195 else {
06196 ast_log(LOG_NOTICE, "Can't join existing call on trunk "
06197 "'%s' due to access controls.\n", trunk_name);
06198 }
06199 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
06200 ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
06201 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06202 return 0;
06203 }
06204
06205 if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME) {
06206 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->hold_stations) == 1)
06207 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06208 else {
06209 trunk_ref->state = SLA_TRUNK_STATE_UP;
06210 ast_devstate_changed(AST_DEVICE_INUSE,
06211 "SLA:%s_%s", station->name, trunk_ref->trunk->name);
06212 }
06213 } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
06214 struct sla_ringing_trunk *ringing_trunk;
06215
06216 ast_mutex_lock(&sla.lock);
06217 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06218 if (ringing_trunk->trunk == trunk_ref->trunk) {
06219 AST_LIST_REMOVE_CURRENT(entry);
06220 break;
06221 }
06222 }
06223 AST_LIST_TRAVERSE_SAFE_END
06224 ast_mutex_unlock(&sla.lock);
06225
06226 if (ringing_trunk) {
06227 answer_trunk_chan(ringing_trunk->trunk->chan);
06228 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06229
06230 free(ringing_trunk);
06231
06232
06233 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06234 sla_queue_event(SLA_EVENT_DIAL_STATE);
06235 }
06236 }
06237
06238 trunk_ref->chan = chan;
06239
06240 if (!trunk_ref->trunk->chan) {
06241 ast_mutex_t cond_lock;
06242 ast_cond_t cond;
06243 pthread_t dont_care;
06244 struct dial_trunk_args args = {
06245 .trunk_ref = trunk_ref,
06246 .station = station,
06247 .cond_lock = &cond_lock,
06248 .cond = &cond,
06249 };
06250 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06251
06252
06253
06254 ast_autoservice_start(chan);
06255 ast_mutex_init(&cond_lock);
06256 ast_cond_init(&cond, NULL);
06257 ast_mutex_lock(&cond_lock);
06258 ast_pthread_create_detached_background(&dont_care, NULL, dial_trunk, &args);
06259 ast_cond_wait(&cond, &cond_lock);
06260 ast_mutex_unlock(&cond_lock);
06261 ast_mutex_destroy(&cond_lock);
06262 ast_cond_destroy(&cond);
06263 ast_autoservice_stop(chan);
06264 if (!trunk_ref->trunk->chan) {
06265 ast_debug(1, "Trunk didn't get created. chan: %lx\n", (long) trunk_ref->trunk->chan);
06266 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
06267 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06268 trunk_ref->chan = NULL;
06269 ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
06270 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06271 return 0;
06272 }
06273 }
06274
06275 if (ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1) == 0 &&
06276 trunk_ref->trunk->on_hold) {
06277 trunk_ref->trunk->on_hold = 0;
06278 ast_indicate(trunk_ref->trunk->chan, AST_CONTROL_UNHOLD);
06279 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06280 }
06281
06282 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
06283 ast_set_flag64(&conf_flags,
06284 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
06285 ast_answer(chan);
06286 conf = build_conf(conf_name, "", "", 0, 0, 1, chan, NULL);
06287 if (conf) {
06288 conf_run(chan, conf, &conf_flags, NULL);
06289 dispose_conf(conf);
06290 conf = NULL;
06291 }
06292 trunk_ref->chan = NULL;
06293 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
06294 trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
06295 strncat(conf_name, ",K", sizeof(conf_name) - strlen(conf_name) - 1);
06296 admin_exec(NULL, conf_name);
06297 trunk_ref->trunk->hold_stations = 0;
06298 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06299 }
06300
06301 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
06302
06303 ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
06304 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06305
06306 return 0;
06307 }
06308
06309 static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
06310 {
06311 struct sla_trunk_ref *trunk_ref;
06312
06313 if (!(trunk_ref = ast_calloc(1, sizeof(*trunk_ref))))
06314 return NULL;
06315
06316 trunk_ref->trunk = trunk;
06317
06318 return trunk_ref;
06319 }
06320
06321 static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
06322 {
06323 struct sla_ringing_trunk *ringing_trunk;
06324
06325 if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk))))
06326 return NULL;
06327
06328 ringing_trunk->trunk = trunk;
06329 ringing_trunk->ring_begin = ast_tvnow();
06330
06331 sla_change_trunk_state(trunk, SLA_TRUNK_STATE_RINGING, ALL_TRUNK_REFS, NULL);
06332
06333 ast_mutex_lock(&sla.lock);
06334 AST_LIST_INSERT_HEAD(&sla.ringing_trunks, ringing_trunk, entry);
06335 ast_mutex_unlock(&sla.lock);
06336
06337 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06338
06339 return ringing_trunk;
06340 }
06341
06342 enum {
06343 SLA_TRUNK_OPT_MOH = (1 << 0),
06344 };
06345
06346 enum {
06347 SLA_TRUNK_OPT_ARG_MOH_CLASS = 0,
06348 SLA_TRUNK_OPT_ARG_ARRAY_SIZE = 1,
06349 };
06350
06351 AST_APP_OPTIONS(sla_trunk_opts, BEGIN_OPTIONS
06352 AST_APP_OPTION_ARG('M', SLA_TRUNK_OPT_MOH, SLA_TRUNK_OPT_ARG_MOH_CLASS),
06353 END_OPTIONS );
06354
06355 static int sla_trunk_exec(struct ast_channel *chan, const char *data)
06356 {
06357 char conf_name[MAX_CONFNUM];
06358 struct ast_conference *conf;
06359 struct ast_flags64 conf_flags = { 0 };
06360 struct sla_trunk *trunk;
06361 struct sla_ringing_trunk *ringing_trunk;
06362 AST_DECLARE_APP_ARGS(args,
06363 AST_APP_ARG(trunk_name);
06364 AST_APP_ARG(options);
06365 );
06366 char *opts[SLA_TRUNK_OPT_ARG_ARRAY_SIZE] = { NULL, };
06367 struct ast_flags opt_flags = { 0 };
06368 char *parse;
06369
06370 if (ast_strlen_zero(data)) {
06371 ast_log(LOG_ERROR, "The SLATrunk application requires an argument, the trunk name\n");
06372 return -1;
06373 }
06374
06375 parse = ast_strdupa(data);
06376 AST_STANDARD_APP_ARGS(args, parse);
06377 if (args.argc == 2) {
06378 if (ast_app_parse_options(sla_trunk_opts, &opt_flags, opts, args.options)) {
06379 ast_log(LOG_ERROR, "Error parsing options for SLATrunk\n");
06380 return -1;
06381 }
06382 }
06383
06384 AST_RWLIST_RDLOCK(&sla_trunks);
06385 trunk = sla_find_trunk(args.trunk_name);
06386 if (trunk)
06387 ast_atomic_fetchadd_int((int *) &trunk->ref_count, 1);
06388 AST_RWLIST_UNLOCK(&sla_trunks);
06389
06390 if (!trunk) {
06391 ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", args.trunk_name);
06392 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06393 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06394 return 0;
06395 }
06396
06397 if (trunk->chan) {
06398 ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
06399 args.trunk_name);
06400 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06401 ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
06402 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06403 return 0;
06404 }
06405
06406 trunk->chan = chan;
06407
06408 if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
06409 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06410 ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
06411 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06412 return 0;
06413 }
06414
06415 snprintf(conf_name, sizeof(conf_name), "SLA_%s", args.trunk_name);
06416 conf = build_conf(conf_name, "", "", 1, 1, 1, chan, NULL);
06417 if (!conf) {
06418 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06419 ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
06420 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06421 return 0;
06422 }
06423 ast_set_flag64(&conf_flags,
06424 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | CONFFLAG_PASS_DTMF | CONFFLAG_NO_AUDIO_UNTIL_UP);
06425
06426 if (ast_test_flag(&opt_flags, SLA_TRUNK_OPT_MOH)) {
06427 ast_indicate(chan, -1);
06428 ast_set_flag64(&conf_flags, CONFFLAG_MOH);
06429 } else
06430 ast_indicate(chan, AST_CONTROL_RINGING);
06431
06432 conf_run(chan, conf, &conf_flags, opts);
06433 dispose_conf(conf);
06434 conf = NULL;
06435 trunk->chan = NULL;
06436 trunk->on_hold = 0;
06437
06438 sla_change_trunk_state(trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06439
06440 if (!pbx_builtin_getvar_helper(chan, "SLATRUNK_STATUS"))
06441 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "SUCCESS");
06442
06443
06444 ast_mutex_lock(&sla.lock);
06445 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06446 if (ringing_trunk->trunk == trunk) {
06447 AST_LIST_REMOVE_CURRENT(entry);
06448 break;
06449 }
06450 }
06451 AST_LIST_TRAVERSE_SAFE_END;
06452 ast_mutex_unlock(&sla.lock);
06453 if (ringing_trunk) {
06454 ast_free(ringing_trunk);
06455 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
06456
06457
06458 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06459 }
06460
06461 ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
06462 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06463
06464 return 0;
06465 }
06466
06467 static enum ast_device_state sla_state(const char *data)
06468 {
06469 char *buf, *station_name, *trunk_name;
06470 struct sla_station *station;
06471 struct sla_trunk_ref *trunk_ref;
06472 enum ast_device_state res = AST_DEVICE_INVALID;
06473
06474 trunk_name = buf = ast_strdupa(data);
06475 station_name = strsep(&trunk_name, "_");
06476
06477 AST_RWLIST_RDLOCK(&sla_stations);
06478 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
06479 if (strcasecmp(station_name, station->name))
06480 continue;
06481 AST_RWLIST_RDLOCK(&sla_trunks);
06482 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06483 if (!strcasecmp(trunk_name, trunk_ref->trunk->name))
06484 break;
06485 }
06486 if (!trunk_ref) {
06487 AST_RWLIST_UNLOCK(&sla_trunks);
06488 break;
06489 }
06490 res = sla_state_to_devstate(trunk_ref->state);
06491 AST_RWLIST_UNLOCK(&sla_trunks);
06492 }
06493 AST_RWLIST_UNLOCK(&sla_stations);
06494
06495 if (res == AST_DEVICE_INVALID) {
06496 ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
06497 trunk_name, station_name);
06498 }
06499
06500 return res;
06501 }
06502
06503 static void destroy_trunk(struct sla_trunk *trunk)
06504 {
06505 struct sla_station_ref *station_ref;
06506
06507 if (!ast_strlen_zero(trunk->autocontext))
06508 ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
06509
06510 while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry)))
06511 ast_free(station_ref);
06512
06513 ast_string_field_free_memory(trunk);
06514 ast_free(trunk);
06515 }
06516
06517 static void destroy_station(struct sla_station *station)
06518 {
06519 struct sla_trunk_ref *trunk_ref;
06520
06521 if (!ast_strlen_zero(station->autocontext)) {
06522 AST_RWLIST_RDLOCK(&sla_trunks);
06523 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06524 char exten[AST_MAX_EXTENSION];
06525 char hint[AST_MAX_APP];
06526 snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
06527 snprintf(hint, sizeof(hint), "SLA:%s", exten);
06528 ast_context_remove_extension(station->autocontext, exten,
06529 1, sla_registrar);
06530 ast_context_remove_extension(station->autocontext, hint,
06531 PRIORITY_HINT, sla_registrar);
06532 }
06533 AST_RWLIST_UNLOCK(&sla_trunks);
06534 }
06535
06536 while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry)))
06537 ast_free(trunk_ref);
06538
06539 ast_string_field_free_memory(station);
06540 ast_free(station);
06541 }
06542
06543 static void sla_destroy(void)
06544 {
06545 struct sla_trunk *trunk;
06546 struct sla_station *station;
06547
06548 AST_RWLIST_WRLOCK(&sla_trunks);
06549 while ((trunk = AST_RWLIST_REMOVE_HEAD(&sla_trunks, entry)))
06550 destroy_trunk(trunk);
06551 AST_RWLIST_UNLOCK(&sla_trunks);
06552
06553 AST_RWLIST_WRLOCK(&sla_stations);
06554 while ((station = AST_RWLIST_REMOVE_HEAD(&sla_stations, entry)))
06555 destroy_station(station);
06556 AST_RWLIST_UNLOCK(&sla_stations);
06557
06558 if (sla.thread != AST_PTHREADT_NULL) {
06559 ast_mutex_lock(&sla.lock);
06560 sla.stop = 1;
06561 ast_cond_signal(&sla.cond);
06562 ast_mutex_unlock(&sla.lock);
06563 pthread_join(sla.thread, NULL);
06564 }
06565
06566
06567 ast_context_destroy(NULL, sla_registrar);
06568
06569 ast_mutex_destroy(&sla.lock);
06570 ast_cond_destroy(&sla.cond);
06571 }
06572
06573 static int sla_check_device(const char *device)
06574 {
06575 char *tech, *tech_data;
06576
06577 tech_data = ast_strdupa(device);
06578 tech = strsep(&tech_data, "/");
06579
06580 if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
06581 return -1;
06582
06583 return 0;
06584 }
06585
06586 static int sla_build_trunk(struct ast_config *cfg, const char *cat)
06587 {
06588 struct sla_trunk *trunk;
06589 struct ast_variable *var;
06590 const char *dev;
06591
06592 if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
06593 ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
06594 return -1;
06595 }
06596
06597 if (sla_check_device(dev)) {
06598 ast_log(LOG_ERROR, "SLA Trunk '%s' define with invalid device '%s'!\n",
06599 cat, dev);
06600 return -1;
06601 }
06602
06603 if (!(trunk = ast_calloc_with_stringfields(1, struct sla_trunk, 32))) {
06604 return -1;
06605 }
06606
06607 ast_string_field_set(trunk, name, cat);
06608 ast_string_field_set(trunk, device, dev);
06609
06610 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
06611 if (!strcasecmp(var->name, "autocontext"))
06612 ast_string_field_set(trunk, autocontext, var->value);
06613 else if (!strcasecmp(var->name, "ringtimeout")) {
06614 if (sscanf(var->value, "%30u", &trunk->ring_timeout) != 1) {
06615 ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for trunk '%s'\n",
06616 var->value, trunk->name);
06617 trunk->ring_timeout = 0;
06618 }
06619 } else if (!strcasecmp(var->name, "barge"))
06620 trunk->barge_disabled = ast_false(var->value);
06621 else if (!strcasecmp(var->name, "hold")) {
06622 if (!strcasecmp(var->value, "private"))
06623 trunk->hold_access = SLA_HOLD_PRIVATE;
06624 else if (!strcasecmp(var->value, "open"))
06625 trunk->hold_access = SLA_HOLD_OPEN;
06626 else {
06627 ast_log(LOG_WARNING, "Invalid value '%s' for hold on trunk %s\n",
06628 var->value, trunk->name);
06629 }
06630 } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
06631 ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
06632 var->name, var->lineno, SLA_CONFIG_FILE);
06633 }
06634 }
06635
06636 if (!ast_strlen_zero(trunk->autocontext)) {
06637 struct ast_context *context;
06638 context = ast_context_find_or_create(NULL, NULL, trunk->autocontext, sla_registrar);
06639 if (!context) {
06640 ast_log(LOG_ERROR, "Failed to automatically find or create "
06641 "context '%s' for SLA!\n", trunk->autocontext);
06642 destroy_trunk(trunk);
06643 return -1;
06644 }
06645 if (ast_add_extension2(context, 0 , "s", 1,
06646 NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free_ptr, sla_registrar)) {
06647 ast_log(LOG_ERROR, "Failed to automatically create extension "
06648 "for trunk '%s'!\n", trunk->name);
06649 destroy_trunk(trunk);
06650 return -1;
06651 }
06652 }
06653
06654 AST_RWLIST_WRLOCK(&sla_trunks);
06655 AST_RWLIST_INSERT_TAIL(&sla_trunks, trunk, entry);
06656 AST_RWLIST_UNLOCK(&sla_trunks);
06657
06658 return 0;
06659 }
06660
06661 static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
06662 {
06663 struct sla_trunk *trunk;
06664 struct sla_trunk_ref *trunk_ref;
06665 struct sla_station_ref *station_ref;
06666 char *trunk_name, *options, *cur;
06667
06668 options = ast_strdupa(var->value);
06669 trunk_name = strsep(&options, ",");
06670
06671 AST_RWLIST_RDLOCK(&sla_trunks);
06672 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
06673 if (!strcasecmp(trunk->name, trunk_name))
06674 break;
06675 }
06676
06677 AST_RWLIST_UNLOCK(&sla_trunks);
06678 if (!trunk) {
06679 ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
06680 return;
06681 }
06682 if (!(trunk_ref = create_trunk_ref(trunk)))
06683 return;
06684 trunk_ref->state = SLA_TRUNK_STATE_IDLE;
06685
06686 while ((cur = strsep(&options, ","))) {
06687 char *name, *value = cur;
06688 name = strsep(&value, "=");
06689 if (!strcasecmp(name, "ringtimeout")) {
06690 if (sscanf(value, "%30u", &trunk_ref->ring_timeout) != 1) {
06691 ast_log(LOG_WARNING, "Invalid ringtimeout value '%s' for "
06692 "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
06693 trunk_ref->ring_timeout = 0;
06694 }
06695 } else if (!strcasecmp(name, "ringdelay")) {
06696 if (sscanf(value, "%30u", &trunk_ref->ring_delay) != 1) {
06697 ast_log(LOG_WARNING, "Invalid ringdelay value '%s' for "
06698 "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
06699 trunk_ref->ring_delay = 0;
06700 }
06701 } else {
06702 ast_log(LOG_WARNING, "Invalid option '%s' for "
06703 "trunk '%s' on station '%s'\n", name, trunk->name, station->name);
06704 }
06705 }
06706
06707 if (!(station_ref = sla_create_station_ref(station))) {
06708 ast_free(trunk_ref);
06709 return;
06710 }
06711 ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
06712 AST_RWLIST_WRLOCK(&sla_trunks);
06713 AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
06714 AST_RWLIST_UNLOCK(&sla_trunks);
06715 AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
06716 }
06717
06718 static int sla_build_station(struct ast_config *cfg, const char *cat)
06719 {
06720 struct sla_station *station;
06721 struct ast_variable *var;
06722 const char *dev;
06723
06724 if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
06725 ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
06726 return -1;
06727 }
06728
06729 if (!(station = ast_calloc_with_stringfields(1, struct sla_station, 32))) {
06730 return -1;
06731 }
06732
06733 ast_string_field_set(station, name, cat);
06734 ast_string_field_set(station, device, dev);
06735
06736 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
06737 if (!strcasecmp(var->name, "trunk"))
06738 sla_add_trunk_to_station(station, var);
06739 else if (!strcasecmp(var->name, "autocontext"))
06740 ast_string_field_set(station, autocontext, var->value);
06741 else if (!strcasecmp(var->name, "ringtimeout")) {
06742 if (sscanf(var->value, "%30u", &station->ring_timeout) != 1) {
06743 ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
06744 var->value, station->name);
06745 station->ring_timeout = 0;
06746 }
06747 } else if (!strcasecmp(var->name, "ringdelay")) {
06748 if (sscanf(var->value, "%30u", &station->ring_delay) != 1) {
06749 ast_log(LOG_WARNING, "Invalid ringdelay '%s' specified for station '%s'\n",
06750 var->value, station->name);
06751 station->ring_delay = 0;
06752 }
06753 } else if (!strcasecmp(var->name, "hold")) {
06754 if (!strcasecmp(var->value, "private"))
06755 station->hold_access = SLA_HOLD_PRIVATE;
06756 else if (!strcasecmp(var->value, "open"))
06757 station->hold_access = SLA_HOLD_OPEN;
06758 else {
06759 ast_log(LOG_WARNING, "Invalid value '%s' for hold on station %s\n",
06760 var->value, station->name);
06761 }
06762
06763 } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
06764 ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
06765 var->name, var->lineno, SLA_CONFIG_FILE);
06766 }
06767 }
06768
06769 if (!ast_strlen_zero(station->autocontext)) {
06770 struct ast_context *context;
06771 struct sla_trunk_ref *trunk_ref;
06772 context = ast_context_find_or_create(NULL, NULL, station->autocontext, sla_registrar);
06773 if (!context) {
06774 ast_log(LOG_ERROR, "Failed to automatically find or create "
06775 "context '%s' for SLA!\n", station->autocontext);
06776 destroy_station(station);
06777 return -1;
06778 }
06779
06780
06781 if (ast_add_extension2(context, 0 , station->name, 1,
06782 NULL, NULL, slastation_app, ast_strdup(station->name), ast_free_ptr, sla_registrar)) {
06783 ast_log(LOG_ERROR, "Failed to automatically create extension "
06784 "for trunk '%s'!\n", station->name);
06785 destroy_station(station);
06786 return -1;
06787 }
06788 AST_RWLIST_RDLOCK(&sla_trunks);
06789 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06790 char exten[AST_MAX_EXTENSION];
06791 char hint[AST_MAX_APP];
06792 snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
06793 snprintf(hint, sizeof(hint), "SLA:%s", exten);
06794
06795
06796 if (ast_add_extension2(context, 0 , exten, 1,
06797 NULL, NULL, slastation_app, ast_strdup(exten), ast_free_ptr, sla_registrar)) {
06798 ast_log(LOG_ERROR, "Failed to automatically create extension "
06799 "for trunk '%s'!\n", station->name);
06800 destroy_station(station);
06801 return -1;
06802 }
06803
06804
06805 if (ast_add_extension2(context, 0 , exten, PRIORITY_HINT,
06806 NULL, NULL, hint, NULL, NULL, sla_registrar)) {
06807 ast_log(LOG_ERROR, "Failed to automatically create hint "
06808 "for trunk '%s'!\n", station->name);
06809 destroy_station(station);
06810 return -1;
06811 }
06812 }
06813 AST_RWLIST_UNLOCK(&sla_trunks);
06814 }
06815
06816 AST_RWLIST_WRLOCK(&sla_stations);
06817 AST_RWLIST_INSERT_TAIL(&sla_stations, station, entry);
06818 AST_RWLIST_UNLOCK(&sla_stations);
06819
06820 return 0;
06821 }
06822
06823 static int sla_load_config(int reload)
06824 {
06825 struct ast_config *cfg;
06826 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06827 const char *cat = NULL;
06828 int res = 0;
06829 const char *val;
06830
06831 if (!reload) {
06832 ast_mutex_init(&sla.lock);
06833 ast_cond_init(&sla.cond, NULL);
06834 }
06835
06836 if (!(cfg = ast_config_load(SLA_CONFIG_FILE, config_flags))) {
06837 return 0;
06838 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
06839 return 0;
06840 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
06841 ast_log(LOG_ERROR, "Config file " SLA_CONFIG_FILE " is in an invalid format. Aborting.\n");
06842 return 0;
06843 }
06844
06845 if (reload) {
06846 struct sla_station *station;
06847 struct sla_trunk *trunk;
06848
06849
06850 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&sla_stations, station, entry) {
06851 AST_RWLIST_REMOVE_CURRENT(entry);
06852 ast_free(station);
06853 }
06854 AST_RWLIST_TRAVERSE_SAFE_END;
06855
06856 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&sla_trunks, trunk, entry) {
06857 AST_RWLIST_REMOVE_CURRENT(entry);
06858 ast_free(trunk);
06859 }
06860 AST_RWLIST_TRAVERSE_SAFE_END;
06861 }
06862
06863 if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
06864 sla.attempt_callerid = ast_true(val);
06865
06866 while ((cat = ast_category_browse(cfg, cat)) && !res) {
06867 const char *type;
06868 if (!strcasecmp(cat, "general"))
06869 continue;
06870 if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
06871 ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
06872 SLA_CONFIG_FILE);
06873 continue;
06874 }
06875 if (!strcasecmp(type, "trunk"))
06876 res = sla_build_trunk(cfg, cat);
06877 else if (!strcasecmp(type, "station"))
06878 res = sla_build_station(cfg, cat);
06879 else {
06880 ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'!\n",
06881 SLA_CONFIG_FILE, type);
06882 }
06883 }
06884
06885 ast_config_destroy(cfg);
06886
06887
06888
06889 if (sla.thread == AST_PTHREADT_NULL && (!AST_LIST_EMPTY(&sla_stations) || !AST_LIST_EMPTY(&sla_trunks))) {
06890 ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
06891 }
06892
06893 return res;
06894 }
06895
06896 static int acf_meetme_info_eval(const char *keyword, const struct ast_conference *conf)
06897 {
06898 if (!strcasecmp("lock", keyword)) {
06899 return conf->locked;
06900 } else if (!strcasecmp("parties", keyword)) {
06901 return conf->users;
06902 } else if (!strcasecmp("activity", keyword)) {
06903 time_t now;
06904 now = time(NULL);
06905 return (now - conf->start);
06906 } else if (!strcasecmp("dynamic", keyword)) {
06907 return conf->isdynamic;
06908 } else {
06909 return -1;
06910 }
06911
06912 }
06913
06914 static int acf_meetme_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
06915 {
06916 struct ast_conference *conf;
06917 char *parse;
06918 int result = -2;
06919 AST_DECLARE_APP_ARGS(args,
06920 AST_APP_ARG(keyword);
06921 AST_APP_ARG(confno);
06922 );
06923
06924 if (ast_strlen_zero(data)) {
06925 ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires two arguments\n");
06926 return -1;
06927 }
06928
06929 parse = ast_strdupa(data);
06930 AST_STANDARD_APP_ARGS(args, parse);
06931
06932 if (ast_strlen_zero(args.keyword)) {
06933 ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a keyword\n");
06934 return -1;
06935 }
06936
06937 if (ast_strlen_zero(args.confno)) {
06938 ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a conference number\n");
06939 return -1;
06940 }
06941
06942 AST_LIST_LOCK(&confs);
06943 AST_LIST_TRAVERSE(&confs, conf, list) {
06944 if (!strcmp(args.confno, conf->confno)) {
06945 result = acf_meetme_info_eval(args.keyword, conf);
06946 break;
06947 }
06948 }
06949 AST_LIST_UNLOCK(&confs);
06950
06951 if (result > -1) {
06952 snprintf(buf, len, "%d", result);
06953 } else if (result == -1) {
06954 ast_log(LOG_NOTICE, "Error: invalid keyword: '%s'\n", args.keyword);
06955 snprintf(buf, len, "0");
06956 } else if (result == -2) {
06957 ast_log(LOG_NOTICE, "Error: conference (%s) not found\n", args.confno);
06958 snprintf(buf, len, "0");
06959 }
06960
06961 return 0;
06962 }
06963
06964
06965 static struct ast_custom_function meetme_info_acf = {
06966 .name = "MEETME_INFO",
06967 .read = acf_meetme_info,
06968 };
06969
06970
06971 static int load_config(int reload)
06972 {
06973 load_config_meetme();
06974
06975 if (reload && sla.thread != AST_PTHREADT_NULL) {
06976 sla_queue_event(SLA_EVENT_RELOAD);
06977 ast_log(LOG_NOTICE, "A reload of the SLA configuration has been requested "
06978 "and will be completed when the system is idle.\n");
06979 return 0;
06980 }
06981
06982 return sla_load_config(0);
06983 }
06984
06985 #define MEETME_DATA_EXPORT(MEMBER) \
06986 MEMBER(ast_conference, confno, AST_DATA_STRING) \
06987 MEMBER(ast_conference, dahdiconf, AST_DATA_INTEGER) \
06988 MEMBER(ast_conference, users, AST_DATA_INTEGER) \
06989 MEMBER(ast_conference, markedusers, AST_DATA_INTEGER) \
06990 MEMBER(ast_conference, maxusers, AST_DATA_INTEGER) \
06991 MEMBER(ast_conference, isdynamic, AST_DATA_BOOLEAN) \
06992 MEMBER(ast_conference, locked, AST_DATA_BOOLEAN) \
06993 MEMBER(ast_conference, recordingfilename, AST_DATA_STRING) \
06994 MEMBER(ast_conference, recordingformat, AST_DATA_STRING) \
06995 MEMBER(ast_conference, pin, AST_DATA_PASSWORD) \
06996 MEMBER(ast_conference, pinadmin, AST_DATA_PASSWORD) \
06997 MEMBER(ast_conference, start, AST_DATA_TIMESTAMP) \
06998 MEMBER(ast_conference, endtime, AST_DATA_TIMESTAMP)
06999
07000 AST_DATA_STRUCTURE(ast_conference, MEETME_DATA_EXPORT);
07001
07002 #define MEETME_USER_DATA_EXPORT(MEMBER) \
07003 MEMBER(ast_conf_user, user_no, AST_DATA_INTEGER) \
07004 MEMBER(ast_conf_user, talking, AST_DATA_BOOLEAN) \
07005 MEMBER(ast_conf_user, dahdichannel, AST_DATA_BOOLEAN) \
07006 MEMBER(ast_conf_user, jointime, AST_DATA_TIMESTAMP) \
07007 MEMBER(ast_conf_user, kicktime, AST_DATA_TIMESTAMP) \
07008 MEMBER(ast_conf_user, timelimit, AST_DATA_MILLISECONDS) \
07009 MEMBER(ast_conf_user, play_warning, AST_DATA_MILLISECONDS) \
07010 MEMBER(ast_conf_user, warning_freq, AST_DATA_MILLISECONDS)
07011
07012 AST_DATA_STRUCTURE(ast_conf_user, MEETME_USER_DATA_EXPORT);
07013
07014 static int user_add_provider_cb(void *obj, void *arg, int flags)
07015 {
07016 struct ast_data *data_meetme_user;
07017 struct ast_data *data_meetme_user_channel;
07018 struct ast_data *data_meetme_user_volume;
07019
07020 struct ast_conf_user *user = obj;
07021 struct ast_data *data_meetme_users = arg;
07022
07023 data_meetme_user = ast_data_add_node(data_meetme_users, "user");
07024 if (!data_meetme_user) {
07025 return 0;
07026 }
07027
07028 ast_data_add_structure(ast_conf_user, data_meetme_user, user);
07029
07030
07031 data_meetme_user_channel = ast_data_add_node(data_meetme_user, "channel");
07032 if (!data_meetme_user_channel) {
07033 return 0;
07034 }
07035
07036 ast_channel_data_add_structure(data_meetme_user_channel, user->chan, 1);
07037
07038
07039 data_meetme_user_volume = ast_data_add_node(data_meetme_user, "listen-volume");
07040 if (!data_meetme_user_volume) {
07041 return 0;
07042 }
07043 ast_data_add_int(data_meetme_user_volume, "desired", user->listen.desired);
07044 ast_data_add_int(data_meetme_user_volume, "actual", user->listen.actual);
07045
07046 data_meetme_user_volume = ast_data_add_node(data_meetme_user, "talk-volume");
07047 if (!data_meetme_user_volume) {
07048 return 0;
07049 }
07050 ast_data_add_int(data_meetme_user_volume, "desired", user->talk.desired);
07051 ast_data_add_int(data_meetme_user_volume, "actual", user->talk.actual);
07052
07053 return 0;
07054 }
07055
07056
07057
07058
07059
07060 static int meetme_data_provider_get(const struct ast_data_search *search,
07061 struct ast_data *data_root)
07062 {
07063 struct ast_conference *cnf;
07064 struct ast_data *data_meetme, *data_meetme_users;
07065
07066 AST_LIST_LOCK(&confs);
07067 AST_LIST_TRAVERSE(&confs, cnf, list) {
07068 data_meetme = ast_data_add_node(data_root, "meetme");
07069 if (!data_meetme) {
07070 continue;
07071 }
07072
07073 ast_data_add_structure(ast_conference, data_meetme, cnf);
07074
07075 if (ao2_container_count(cnf->usercontainer)) {
07076 data_meetme_users = ast_data_add_node(data_meetme, "users");
07077 if (!data_meetme_users) {
07078 ast_data_remove_node(data_root, data_meetme);
07079 continue;
07080 }
07081
07082 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_add_provider_cb, data_meetme_users);
07083 }
07084
07085 if (!ast_data_search_match(search, data_meetme)) {
07086 ast_data_remove_node(data_root, data_meetme);
07087 }
07088 }
07089 AST_LIST_UNLOCK(&confs);
07090
07091 return 0;
07092 }
07093
07094 static const struct ast_data_handler meetme_data_provider = {
07095 .version = AST_DATA_HANDLER_VERSION,
07096 .get = meetme_data_provider_get
07097 };
07098
07099 static const struct ast_data_entry meetme_data_providers[] = {
07100 AST_DATA_ENTRY("asterisk/application/meetme/list", &meetme_data_provider),
07101 };
07102
07103 #ifdef TEST_FRAMEWORK
07104 AST_TEST_DEFINE(test_meetme_data_provider)
07105 {
07106 struct ast_channel *chan;
07107 struct ast_conference *cnf;
07108 struct ast_data *node;
07109 struct ast_data_query query = {
07110 .path = "/asterisk/application/meetme/list",
07111 .search = "list/meetme/confno=9898"
07112 };
07113
07114 switch (cmd) {
07115 case TEST_INIT:
07116 info->name = "meetme_get_data_test";
07117 info->category = "/main/data/app_meetme/list/";
07118 info->summary = "Meetme data provider unit test";
07119 info->description =
07120 "Tests whether the Meetme data provider implementation works as expected.";
07121 return AST_TEST_NOT_RUN;
07122 case TEST_EXECUTE:
07123 break;
07124 }
07125
07126 chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL, NULL, 0, 0, "MeetMeTest");
07127 if (!chan) {
07128 ast_test_status_update(test, "Channel allocation failed\n");
07129 return AST_TEST_FAIL;
07130 }
07131
07132 cnf = build_conf("9898", "", "1234", 1, 1, 1, chan, test);
07133 if (!cnf) {
07134 ast_test_status_update(test, "Build of test conference 9898 failed\n");
07135 ast_hangup(chan);
07136 return AST_TEST_FAIL;
07137 }
07138
07139 node = ast_data_get(&query);
07140 if (!node) {
07141 ast_test_status_update(test, "Data query for test conference 9898 failed\n");
07142 dispose_conf(cnf);
07143 ast_hangup(chan);
07144 return AST_TEST_FAIL;
07145 }
07146
07147 if (strcmp(ast_data_retrieve_string(node, "meetme/confno"), "9898")) {
07148 ast_test_status_update(test, "Query returned the wrong conference\n");
07149 dispose_conf(cnf);
07150 ast_hangup(chan);
07151 ast_data_free(node);
07152 return AST_TEST_FAIL;
07153 }
07154
07155 ast_data_free(node);
07156 dispose_conf(cnf);
07157 ast_hangup(chan);
07158
07159 return AST_TEST_PASS;
07160 }
07161 #endif
07162
07163 static int unload_module(void)
07164 {
07165 int res = 0;
07166
07167 ast_cli_unregister_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
07168 res = ast_manager_unregister("MeetmeMute");
07169 res |= ast_manager_unregister("MeetmeUnmute");
07170 res |= ast_manager_unregister("MeetmeList");
07171 res |= ast_unregister_application(app4);
07172 res |= ast_unregister_application(app3);
07173 res |= ast_unregister_application(app2);
07174 res |= ast_unregister_application(app);
07175 res |= ast_unregister_application(slastation_app);
07176 res |= ast_unregister_application(slatrunk_app);
07177
07178 #ifdef TEST_FRAMEWORK
07179 AST_TEST_UNREGISTER(test_meetme_data_provider);
07180 #endif
07181 ast_data_unregister(NULL);
07182
07183 ast_devstate_prov_del("Meetme");
07184 ast_devstate_prov_del("SLA");
07185
07186 sla_destroy();
07187
07188 res |= ast_custom_function_unregister(&meetme_info_acf);
07189 ast_unload_realtime("meetme");
07190
07191 return res;
07192 }
07193
07194
07195
07196 static int load_module(void)
07197 {
07198 int res = 0;
07199
07200 res |= load_config(0);
07201
07202 ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
07203 res |= ast_manager_register_xml("MeetmeMute", EVENT_FLAG_CALL, action_meetmemute);
07204 res |= ast_manager_register_xml("MeetmeUnmute", EVENT_FLAG_CALL, action_meetmeunmute);
07205 res |= ast_manager_register_xml("MeetmeList", EVENT_FLAG_REPORTING, action_meetmelist);
07206 res |= ast_register_application_xml(app4, channel_admin_exec);
07207 res |= ast_register_application_xml(app3, admin_exec);
07208 res |= ast_register_application_xml(app2, count_exec);
07209 res |= ast_register_application_xml(app, conf_exec);
07210 res |= ast_register_application_xml(slastation_app, sla_station_exec);
07211 res |= ast_register_application_xml(slatrunk_app, sla_trunk_exec);
07212
07213 #ifdef TEST_FRAMEWORK
07214 AST_TEST_REGISTER(test_meetme_data_provider);
07215 #endif
07216 ast_data_register_multiple(meetme_data_providers, ARRAY_LEN(meetme_data_providers));
07217
07218 res |= ast_devstate_prov_add("Meetme", meetmestate);
07219 res |= ast_devstate_prov_add("SLA", sla_state);
07220
07221 res |= ast_custom_function_register(&meetme_info_acf);
07222 ast_realtime_require_field("meetme", "confno", RQ_UINTEGER2, 3, "members", RQ_UINTEGER1, 3, NULL);
07223
07224 return res;
07225 }
07226
07227 static int reload(void)
07228 {
07229 ast_unload_realtime("meetme");
07230 return load_config(1);
07231 }
07232
07233 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "MeetMe conference bridge",
07234 .load = load_module,
07235 .unload = unload_module,
07236 .reload = reload,
07237 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
07238 );
07239